1. Introduction

One of our teammates lives in Hamilton Heights, in an ‘up and coming’ area called Sugar Hill which has seen a lot of new bars and restaurants open up (we highly suggest checking out Harlem Public). We were wondering how one could easily identify an ‘up and coming’ area before all the land values rise. Could they be identified by new winebars and pubs? Or perhaps by sidewalk cafes that pop up as soon as the weather gets warmer?

We found a dataset for liquor licenses in New York State at data.ny.gov. Called “Liquor Authority Quarterly List of Active Licenses”, this dataset gives information about all of the currently active liquor licenses as of April, 2017.

We also found a dataset about all requested and active sidewalk cafe license applications in New York City (and the surrounding boroughs excluding Long Island) called “Sidewalk Café Licenses and Applications”. This has also been updated in April, 2017.

Finally, we wanted to know whether there is a clear correlation between the average income of neighborhood inhabitants and the number of sidewalk cafes and bars in that area. To do this, we used the following datasets:

We realized that there is no authoritative datasource mapping each zip code to a single neighborhood. In order to be able to map neighborhood locations on a map, we wrote a python scraper to pull zipcode to neighborhood data from the following locations, and then joined it with the zipcode to location data:

2. Team - Adam

List team members and a description of how each contributed to the project. (If you’re working alone, briefly describe the stages of the project.)

  1. Adam Coviensky -
    Adam was mostly in charge of the income per zipcode data. He found and analyzed the original property value dataset which we did not end up using once we determined that it would be hard to map the neighborhoods and cafe and liquor license data without zipcodes. He found and cleaned new superzip dataset and helped join the zipcodes to neighborhoods. He also did the zipcode shapefile graphs by income and wrote the writeup corresponding to his sections. He helped to combine the analysis and make observations.

  2. Rohan Pitre -

  3. Marika Lohmus -

require(ggplot2)
require(dplyr)
require(tidyr)
require(extracat)
require(forcats)
require(ggmosaic)
require(ggmap)
require(gridExtra)
require(ggrepel)
require(lubridate)
require(shiny)
require(zipcode)
require(viridis)
require(rgdal)
require(maptools)

3. Analysis of Data Quality - All

Zip Code, Neighborhood and Land Value Data - Adam

We realized that the original data was not going to be sufficient. We only had a few rental observations from Staten Island, Queens and the Bronx, and all of the observations were directly bordering Manhattan. We decided to find new data. We found the data for a Shiny project titled Superzip with Income per zipcode for the entire United States. Since we also wanted the income per neighborhood, we found a mapping from zipcode to neighborhood name that we will use for our final observations. It proved rather difficult to actually clean this mapping.

superzip <- read.table('Data/superzip.csv', header = TRUE)
neighborhood <- read.csv('Data/nyc_zcta.csv')
colnames(neighborhood)[1] <- "zipcode"
neighborhood2 <- neighborhood[,c(1, 6)]
neighborhood2$zipcode <- as.factor(neighborhood2$zipcode)
sidewalks$CITY[sidewalks$CITY=="LONG IS CITY"]<-"LONG ISLAND CITY"
sidewalks$CITY[sidewalks$CITY=="MIDDLE VLG"]<-"MIDDLE VILLAGE"
sidewalks$CITY[sidewalks$CITY=="JACKSON HTS"]<-"JACKSON HEIGHTS"
sidewalks$CITY[sidewalks$CITY=="New York"]<-"NEW YORK"

Without filtering for the state == NY, we found we had some extra rows. When we looked up these zipcodes online, we found that there were 6 other Brooklyns in the United States. Thus, we had to first filter for NY, then only containing the boroughs. Then, we changed the borough from New York to be Manhattan

master_zip <- full_join(superzip3, neighborhood2, by = 'zipcode')
joining factors with different levels, coercing to character vector
master_zip[!complete.cases(master_zip),]
colnames(master_zip)[11] <- "area"

This shows us the rows which have NAs. As we can clearly see, there were more zipcodes in the neighborhood data than in the superzip data. Our way of adjusting for this will be to determine an average income for each neighborhood. Then, we will be imputing the missing incomes using the average incomes for the zipcodes which we do have in that neighborhood.

AVG_INCOME_NEIGHBORHOOD <- master_zip %>% group_by(area) %>% summarise(avg = mean(income., na.rm=TRUE))

Upon viewing the output, we still have many NAs in the avg column, when looking at the master_zip dataframe filtered for “Astoria & Long Island City”, which is the first neighborhood with an avg of NA, we see that it’s because we have no income data for any zipcodes in this area. Therefore, once we join the average income with the master_zip dataframe we will drop these rows with NAs.

sidewalks %>% filter(LONGITUDE < -77) %>% select(BUSINESS_NAME2,LONGITUDE,LATITUDE)

Here we have joined the average income per neighborhood data back to the rest of the income data.

data(zipcode)
census <- merge(master_zip2, zipcode, by.x = 'zipcode', by.y = 'zip')
census <- census %>% filter(!is.na(avg))

Here we are using the package zipcode to map all of our zipcodes to geospatial coordinates so that we can plot them using ggmap. Then, we are filtering out all of the zipcodes and neighborhoods for which we have no income data at all within that neighborhood.

missing_zips <- c(10065, 10075, 10106, 10281, 11101, 11102, 11103, 11104, 11105, 11106, 11109, 11249, 11366, 11367, 11368, 11370, 11372, 11373, 11374, 11375, 11377, 11379, 11435, 11694)
filtered_missing_zips <- filter(superzip, zipcode %in% missing_zips)
setdiff(missing_zips, filtered_missing_zips$zipcode)
[1] 10065 10075 10106 10281 11109 11249

Here, Marika had informed me that we had missing zipcodes in the final census dataframe. Therefore, I went back to the superzip dataset and found that 18 of the 24 zipcodes appeared there. However, they were labeled as being in different cities, not one of the five boroughs. Since she has data in these zipcodes, we will be adding them back into the census dataframe. The 18 zipcodes which we had data on corresponded mostly to data in Queens which were not labelled as Queens. This also explains the lack of any income data in some of the neighborhoods.

We also found that 6 zipcodes are still missing from our income data. These are 10065 (Upper East Side), 10075(Upper East Side), 10106(midtown), 10281 (World Trade), 11109 (Long Island) and 11249(Williamsburg).

superzip4 <- rbind(superzip3, filtered_missing_zips)
master_zip_added <- full_join(superzip4, neighborhood2, by = 'zipcode')
joining factors with different levels, coercing to character vector
master_zip_added[!complete.cases(master_zip_added),]
colnames(master_zip_added)[11] <- "area"
AVG_INCOME_NEIGHBORHOOD_ADDED <- master_zip_added %>% group_by(area) %>% summarise(avg = mean(income., na.rm=TRUE))
master_zip2_added <- left_join(master_zip_added, AVG_INCOME_NEIGHBORHOOD_ADDED, by = "area")
census_added <- merge(master_zip2_added, zipcode, by.x = 'zipcode', by.y = 'zip')
census_added2 <- census_added %>% filter(!is.na(avg))
write.csv(census_added2, file = "zip_master_no_missing.csv")

Here I went back and added in the missing zip codes. I then had to recompute the average statistic accounting for the new zipcodes. I repeated the same process as before once I added the zipcodes back in.

Now we are loading back in the good data after writing some to the folder.

missing_expiration<-sidewalks %>% filter(is.na(EXPIRATION_DATE)) %>% select(APP_STATUS)
ggplot(missing_expiration,aes(x=APP_STATUS))+geom_bar()+ggtitle("In Review licenses have a missing expiration date")

ggplot(census, aes(reorder(city.x, -avg, FUN = median), avg)) + geom_boxplot(varwidth = TRUE) + ggtitle("Boxplots of Income by Borough") + labs(x="Borough", y="Average Income per neighborhood")

Here we see the distributions of the average income per area in each of the 5 boroughs. This plot appears to show that Queens Villge does not have very much data in it as this is a variable width boxplot. However, it turns out it is because all of the other single point neighborhoods are actually a part of Queens. This is a problem we had to fix. These are the 18 zipcodes we added back into the data.

census$city.x[!(census$city.y %in% c("Brooklyn", "New York", "Bronx", "Staten Island"))] <- "Queens Village"
census$city.y[!(census$city.y %in% c("Brooklyn", "New York", "Bronx", "Staten Island"))] <- "Queens Village"
ggplot(census, aes(reorder(city.x, -avg, FUN = median), avg)) + geom_boxplot(varwidth = TRUE) + ggtitle("Boxplots of Income by Borough") + labs(x="Borough", y="Average Income per neighborhood")

Also, there is very little spread in the distribution for Bronx and Brooklyn compared to Manhattan, which has a massive spread. The missing values occur from zipcodes which occur in the neighborhood data and not in the superzip data. We can see that the average income calculated for these zipcodes in general are high. We can thus infer they are likely from New York and looking back at the census data and filtering for the NAs, which was indeed the case.

ggplot(census, aes(reorder(city.y, -avg, FUN = median), avg)) + geom_boxplot(varwidth = TRUE) + ggtitle("Boxplots of Income by Borough") + labs(x="Borough", y="Average Income per neighborhood")

Here we see with the plot with the null values removed.

census %>% filter(!is.na(census$city.x)) %>% ggplot(aes(city.x, income., color=city.x, alpha(0.1))) + geom_point(size = 2, shape = 1) + guides(color=FALSE) + ggtitle("Strip Plot of Incomes by Borough") + labs(x="Borough", y="Income")

Similarly to the boxplot, this shows the distribution of the income for each of the boroughs. We can see we do not have too much data on Staten Island again.

ggplot(census, aes(x=reorder(city.x, -table(city.x)[city.x]))) + geom_bar() + xlab("City") + ylab("Number of Zipcodes") + ggtitle("Number of zipcodes with observations per borough")

Again the NAs here correspond to not having an actual income value for said zipcode. It was derived from the average for that neighborhood. This shows that approximately 25 of the zipcodes had no income data and we were forced to impute it with the average for that neighborhood. Also, this data is certainly better than the last set, although ideally we would still have more data on Queens and Staten Island especially if there are many liquor licenses and sidewalk cafes popping up in zipcodes which we are missing.

Sidewalk Cafe License Data - Marika

queens_names<-read.csv("Data/QU_Locations.csv",strip.white=TRUE)
brooklyn_names<-read.csv("Data/BK_Locations.csv",strip.white=TRUE)
manhattan_names<-read.csv("Data/MA_Locations.csv",strip.white=TRUE)
bronx_names<-read.csv("Data/BX_Locations.csv",strip.white=TRUE)
sidewalks<-read.csv("Data/Sidewalk Cafes/Sidewalk_Licenses.csv",strip.white=TRUE, na.strings=c("","NA"))
Data Quality

One of the most glaring issues in data quality is the naming of the cities in Manhattan and Queens. Capitalization differences like between “NEW YORK” and “New York” grouped cafes in the same city to be categorized differently. Similarly, cities in Queens had some abbreviation differences like between “LONG ISLAND CITY” and “LONG IS CITY” or “JACKSON HEIGHTS” and “JACKSON HTS”. I manually replaced all abbreviated or non-capitalized cities with their longer, capitalized forms.

ggplot(sidewalks, aes(x=fct_infreq(BOROUGH)))+geom_bar(aes(fill=BOROUGH))+ggtitle("Frequency of Sidewalk Cafe Licenses by Borough")+xlab("Borough")+ylab("Frequency")

The second data quality issue I encountered was the longitude and latitude of two restaurants. Plotting all restaurants using qmplot, I got the following result:

qmplot(LONGITUDE,LATITUDE,data=sidewalks,maptype="toner-lite",color=I("purple"))
Using zoom = 9...

Note that there is a mysterious dot all the way west of Harrisburg, PA. This should not be the case since the data should only apply to the City and boroughs of New York, so I plotted the longitude and latitude to see any outliers.

lat<-ggplot(sidewalks, aes(x=LATITUDE))+geom_histogram(binwidth=.01)+ggtitle("Latitude Histogram")
long<-ggplot(sidewalks,aes(x=LONGITUDE))+geom_histogram(binwidth=.01)+ggtitle("Longitude Histogram")
grid.arrange(lat,long,nrow=2)

You can barely see some points beow the 40.25 latitude and -77 longitude, which seem to be quite off from the rest. Next, I zoomed in on those values:

lat<-ggplot(sidewalks, aes(x=LATITUDE))+geom_histogram(binwidth=.25)+ggtitle("Zoomed Latitude Histogram")+xlim(39,40.25)
long<-ggplot(sidewalks,aes(x=LONGITUDE))+geom_histogram(binwidth=.25)+ggtitle("Zoomed Longitude Histogram")+xlim(-80,-77)
grid.arrange(lat,long,nrow=2)

There are two datapoints that have an abnormally low Latitude and Longitude as compared to the rest of the data. Taking a look at these data points, I identified them as the following businesses:

sidewalks %>% filter(LONGITUDE < -77) %>% select(BUSINESS_NAME2,LONGITUDE,LATITUDE)

Looking these locations up on Google, it looks like there was an error in entering their longitude and latitude. The correct values are (40.72017,-73.9968) for Mulberry Street Bar and (40.79593,-73.9357) for Prime One 16. I have corrected these values in another CSV, which I will use from here on.

sidewalks<-read.csv("Data/Sidewalk Cafes/Sidewalk_Licenses2.csv",strip.white=TRUE, na.strings=c("","NA"))
sidewalks$CITY[sidewalks$CITY=="LONG IS CITY"]<-"LONG ISLAND CITY"
sidewalks$CITY[sidewalks$CITY=="MIDDLE VLG"]<-"MIDDLE VILLAGE"
sidewalks$CITY[sidewalks$CITY=="JACKSON HTS"]<-"JACKSON HEIGHTS"
sidewalks$CITY[sidewalks$CITY=="New York"]<-"NEW YORK"
qmplot(LONGITUDE,LATITUDE,data=sidewalks,maptype="toner-lite",color=I("purple"))
Using zoom = 12...

Now that the offending datapoints have been fixed, the qmplot accurately zooms in on the New York area. However, since those two data points exist, it is entirely possible that there are other mis-mapped langitude and longitude points. However, without manually going through each data point, it is not easy to deduce where.

Missing Data

To further investigate data quality, I took a look at the main columns that identified a business - its license number, license status, business name (broken into two), building number, street, city, state and zip. To display this data, I used a visna plot which highlights any columns with missing values and shows the frequency of the combinations of those columns.

visna(sidewalks[,1:9])

The visna shows that only two variables have missing values - license number and business name number 2. These are expected - only licenses that have been approved would have a license number, and some businesses do not need to use a second line for their business name.

Next, I looked at additional application information which includes the sidewalk cafe type, the square footage requested, number of tables, number of chairs, Department of Health and Mental Hygiene (DOHMH) identification number, the restaurant’s longitude and latitude, the community district and city council district it belongs to, and the URL for the website of the NYC Community District.

visna(sidewalks[,10:19])

Two variables have missing values - the square footage of the sidewalk cafe and the Department of Health and Mental Hygene identification number. These are items that the restaurant would have to provide during their application process, and may not have been available at submission. These missing values should not impede with our analysis. However, if we wanted to cross-reference the health grades from the DOHMH datasets, we would have issues with the missing ID numbers.

The next set of columns to analyze is the set of application-specific fields. These include the application ID, and the sidewalk cafe type, square footage, number of tables and chairs provided by the applicant. It has the app status, the date at which the app status was reached, expiration date, the expiration date of the temporary operating order(“TOO” - if available), the application submit date, and whether the application has been received.

visna(sidewalks[,20:29])

There seem to be a few missing fields in the provided sidewalk cafe squarefootage data. However, we will not be focusing on this data point in our analysis. Expiration dates are also missing from certain licenses. Filtering out those licenses and looking at thei APP_STATUS, we can see that those licenses that are in review may not have an assigned expiration status. Indeed, this makes sense with new applications that have not been given an expiration date.

missing_expiration<-sidewalks %>% filter(is.na(EXPIRATION_DATE)) %>% select(APP_STATUS)
missing_expiration %>% unique()

A similar explanation arises about the applications with a missing Temporary Operating Order (“TOO”) date - only those applications that have been granted a TOO in cases where the old license has expired but the renewal is in the process of being reviewed.

The remaining colums track the status of the license approval process through various stages and will not be used in this analysis. Therefore, I will not spend time on investigating any missing data.

Combining with Neighborhood data

From Adam’s analysis, we have a master_zip list of all of the zip codes mapped to the neighborhoods in each borough. I will create a separate dataframe called sidewalks_nbh which is joined with the master_zip on Zip Code. I will use the neighborhood column, ‘nbh’, to group the sidewalk cafes by borough and neighborhood. I then filtered the resulting dataframe to find any missing values from the join.

master_zip<-read.csv("Data/zips_master_no_missing_nbrh.csv", strip.white=TRUE)
sidewalks_nbh <- merge(sidewalks, master_zip, by="ZIP", all.x=TRUE)
sidewalks_nbh %>% filter(is.na(nbh)) %>% select(ZIP)

There were a lot of missing values originally from the master_zip analysis, which I have manually researched and filled in in the _nbrh.csv. Now there are no Cafes without a neighborhood designation.

Liquor License Data - Rohan

4. Executive Summary - All

Interactive Map of New York City

Provide a short nontechnical summary of the most revealing findings of your analysis with no more than 3 static graphs or one interactive graph (or link), written for a nontechnical audience. The length should be approximately 2 pages (if we were using pages…) Do not show code, and take extra care to clean up your graphs, ensuring that best practices for presentation are followed. . Note: the tips below are not intended to be a complete list of everything we’ve covered this semester on designing a successful graph. It’s meant to help you avoid some common problems. . Title, axis labels, tick mark labels, and legends should be comprehensible (easy to understand) and legible (easy to read / decipher). . Tick marks should not be labeled in scientific notation or with long strings of zeros, such as 3000000000. Instead, convert to smaller numbers and change the units: 3000000000 becomes “3” and the axis label “billions of views”. . Units should be intuitive (Extreme example: an axis labeled in month/day/year format is intuitive, one labeled in seconds since January 1, 1970 is not.) . The font size should be large enough to read clearly. The default in ggplot2 is generally too small. You can easily change it by passing the base font size to the theme, such as + theme_grey(16). (The default base font size is 11.) . The order of items on the axes and legends is logical. (Alphabetical is often not logical.) . Colors should be color vision deficiency friendly. . If a legend is taking up too much space on the right, move it to the bottom. . If categorical variable levels are long, set up the graph so the categorical variable is on the y-axis and the names are horizontal. A better option, if possible, is to shorten the names of the levels. . Not all EDA graphs lend themselves to presentation, either because the graph form is hard to understand without practice or it’s not well labeled. The labeling problem can be solved by adding text in an image editor. The downside is that it is not reproducible. If you want to go this route, Paintbrush is a free and simple bitmap image editor for the Mac: https://paintbrush.sourceforge.io/ There are many other options.

5. Main Analysis - All

Provide a detailed, well-organized description of your findings, including textual description, graphs, and code. Your focus should be on both the results and the process. Include, as reasonable and relevant, approaches that didn’t work, challenges, the data cleaning process, etc. . The guidelines for the Executive Summary above do NOT apply to exploratory data analysis. Your main concern is designing graphs that reveal patterns and trends. . As noted in Hmk #4, do not use circles, that is: bubbles, pie charts, or polar coordinates. . Use stacked bar charts sparingly. Try grouped bar charts and faceting as alternatives, and only choose stacked bar charts if they truly do a better job than the alternatives for observing patterns.

Zip Code, Neighborhood and Land Value Data - Adam

One of our other main questions in this project was looking at how the wealth was distributed amongst each of the zipcodes in the five boroughs. Using the census data from the superzip dataset and mapping that to the zipcode shapefile document we found, we managed to visualize the wealth of each neighborhood. We will later be able to plot the license applications over this mapping of the wealth distribution.

map <- get_map("New York City", source = "google", maptype = "toner", zoom = 11)
maptype = "toner" is only available with source = "stamen".
resetting to source = "stamen"...
Source : https://maps.googleapis.com/maps/api/staticmap?center=New+York+City&zoom=11&size=640x640&scale=2&maptype=terrain
Source : https://maps.googleapis.com/maps/api/geocode/json?address=New%20York%20City
Source : http://tile.stamen.com/toner/11/601/768.png
Source : http://tile.stamen.com/toner/11/602/768.png
Source : http://tile.stamen.com/toner/11/603/768.png
Source : http://tile.stamen.com/toner/11/604/768.png
Source : http://tile.stamen.com/toner/11/601/769.png
Source : http://tile.stamen.com/toner/11/602/769.png
Source : http://tile.stamen.com/toner/11/603/769.png
Source : http://tile.stamen.com/toner/11/604/769.png
Source : http://tile.stamen.com/toner/11/601/770.png
Source : http://tile.stamen.com/toner/11/602/770.png
Source : http://tile.stamen.com/toner/11/603/770.png
Source : http://tile.stamen.com/toner/11/604/770.png
Source : http://tile.stamen.com/toner/11/601/771.png
Source : http://tile.stamen.com/toner/11/602/771.png
Source : http://tile.stamen.com/toner/11/603/771.png
Source : http://tile.stamen.com/toner/11/604/771.png
ggmap(map, base_layer = ggplot(aes(x = longitude, y = latitude, color = avg), data = census))  + geom_point(size = 3, alpha=0.7) + scale_color_viridis() + ggtitle("Average Salary of each Zipcode in New York")

This map shows all of the zipcodes and we can clearly see that by far the greatest incomes per zipcode are in the Upper East Side and Upper West Side. There appears to be some grey areaa if you look closely at the Upper West Side. This corresponds to about 150 on the color map. Then, midtown and downtown also have high incomes. After that, they all fall towards the bottom of the spectrum. Thus, I think it would be useful to make a map showing only Manhattan, and then the everything excluding Manhattan. This should give us a better idea of what areas outside of Manhattan might see more bar/cafe openings.

year_bxp<-ggplot(sidewalks_nbh, aes(y=SUBMIT_YEAR,x=1))+geom_boxplot()+ggtitle("Boxplot of Submission Years")
year_bar<-ggplot(sidewalks_nbh,aes(x=SUBMIT_YEAR))+geom_bar()+ggtitle("Bar Graph of Submission Years")
grid.arrange(year_bxp, year_bar, ncol=2)

This graph shows all of the data filtered for Manhattan only. We can see that once we get to Harlem and above, the average median income per neighborhood drops heavily. The rest of the city is pretty similar.

brooklyn_map2 <- get_map("Brooklyn, NY",  source = "google", zoom = 12, maptype="toner", color="bw")
maptype = "toner" is only available with source = "stamen".
resetting to source = "stamen"...
Source : https://maps.googleapis.com/maps/api/staticmap?center=Brooklyn,+NY&zoom=12&size=640x640&scale=2&maptype=terrain
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Brooklyn%2C%20NY
Source : http://tile.stamen.com/toner/12/1205/1539.png
Source : http://tile.stamen.com/toner/12/1206/1539.png
Source : http://tile.stamen.com/toner/12/1207/1539.png
Source : http://tile.stamen.com/toner/12/1205/1540.png
Source : http://tile.stamen.com/toner/12/1206/1540.png
Source : http://tile.stamen.com/toner/12/1207/1540.png
Source : http://tile.stamen.com/toner/12/1205/1541.png
Source : http://tile.stamen.com/toner/12/1206/1541.png
Source : http://tile.stamen.com/toner/12/1207/1541.png
census_brooklyn <- census %>% dplyr:: filter(city.x == 'Brooklyn')
ggmap(brooklyn_map2, base_layer = ggplot(aes(x = longitude, y = latitude, color = avg), data = census_brooklyn))  + geom_point(size = 4, alpha=0.7) + scale_color_viridis() + ggtitle("Average Salary of each Zipcode in Brooklyn")

This shows us the average salary throughout the zipcodes in Brooklyn and we can see that it is highest in general when the neighborhood is closer to Manhattan.

At this point, I had realized that it would be hard to make a combined map illustrating both the income data and licenses data by displaying the zips like this. Thus, I found zipcode shapefile data for NYC and imported it in order to make better maps to be used as a base layer for the licensing data. I also begun labelling the neighborhoods using the labels Marika had created.

plotting_zips1<-read.csv("Data/NY_shapefiles.csv")
map2016<-ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan%>%filter(SUBMIT_YEAR=="2016"), geom="polygon", alpha=0.3)+scale_fill_gradient(low="yellow",high="red")+ggtitle("Manhattan Licenses Applied for in 2016")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)
map2016

Here we see that Manhattan has the highest wealth. It has the wealthiest areas but also some areas in Harlem and more uptown that are less wealthy than most neighborhoods in Queens, Brooklyn and the Bronx.

g_all <- ggmap(map) + geom_polygon(aes(fill = avg, x = long, y = lat, group = group), data = plotting_zips1, alpha = 0.8)
g_all + ggtitle("Distribution of Wealth in New York") + scale_fill_viridis()

Here we see a full distribution using the viridis color scheme. This will be used as the base for the overlaid plots later on and we will then filter by each borough.

Wealth by borough - Adam
plotting_zips_manhattan <- plotting_zips1 %>% filter(city.y == "New York")
plotting_zips_brooklyn <- plotting_zips1 %>% filter(city.y == "Brooklyn")
plotting_zips_bronx <- plotting_zips1 %>% filter(city.y == "Bronx")
plotting_zips_queens <- plotting_zips1 %>% filter(!(city.y %in% c("New York", "Brooklyn", "Bronx", "Staten Island")))
#plotting_zips_staten <- plotting_zips1 %>% filter(city.x !%in% c("Manhattan", "Brooklyn", "Bronx", "Queens Village"))
bronx_map <- get_map("Bronx, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw")
Source : https://maps.googleapis.com/maps/api/staticmap?center=Bronx,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Bronx%2C%20NY
brooklyn_map <- get_map("Brooklyn, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=Brooklyn,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Brooklyn%2C%20NY
queens_map <- get_map("Astoria, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=Astoria,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Astoria%2C%20NY

We are not using Staten Island since we have no sidewalk cafe data in Staten Island

g_manhattan <- ggmap(map) + geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_manhattan, alpha = 0.5)
g_manhattan + ggtitle("Distribution of Wealth in Manhattan") + geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3) + scale_fill_viridis()

This map show us the wealth in Manhattan alone by zipcode. We are now using the viridis color scale so that it is perceptually uniform and easier to see differences in color. This clearly shows that the income is very low at Harlem and above, including Morningside Heights. This is likely due to the fact that it is almost exclusively students living in Morningside Heights and Harlem is cheaper. Also, it is interesting to see that Lower East Side and has a similar income to Harlem. The wealthiest areas are by far Upper West Side and Upper East Side. We can also see that we are missing what seems to be data for two zipcodes in the Upper East side. This corresponds to some of the data that Marika had found was missing and still did not appear in the superzip dataset.

g_brooklyn <- ggmap(brooklyn_map) + geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_brooklyn, alpha = 0.5)
g_brooklyn + ggtitle("Distribution of Wealth in Brooklyn") + geom_text_repel(data=brooklyn_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3) + scale_fill_viridis()

By observing the income distribution in Brooklyn, we see that in general, the areas closer to Manhattan have the highest income such as Brooklyn Heights and Crown Heights.

g_bronx <- ggmap(bronx_map) + geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_bronx, alpha = 0.5)
g_bronx + ggtitle("Distribution of Wealth in Bronx") + geom_text_repel(data=bronx_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3) + scale_fill_viridis()

g_queens <- ggmap(queens_map) + geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_queens, alpha = 0.5)
g_queens + ggtitle("Distribution of Wealth in Queens") + geom_text_repel(data=queens_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3) + scale_fill_viridis()

Surprisingly, it seems that in Queens, some of the higher income areas actually appear further away from Manhattan like in Flushing and Forest Hills and Rego Park. However, there are some higher income areas closer to Manhattan as well like East Elmhurst.

Sidewalk Cafe License Data - Marika

Any business that operates a portion of a restaurant on a public sidewalk must obtain a Sidewalk Cafe License from New Yor City. These licenses must be renewed every two years and fall into three categories: enclosed, unenclosed, or small unenclosed sidewalk cafes.

Analyzing by Borough

First, to help better organize the sidewalk cafe licenses by borough, I added a new column called BOROUGH that is set to MANHATTAN, BROOKLYN, BRONX, or QUEENS. I had to manually check that only the cities in Queens had been called out specifically in the CITY column, so it was easy to distinguish them from BRONX or BROOKLYN.

sidewalks <- sidewalks %>% mutate(BOROUGH = ifelse(CITY=="NEW YORK"|CITY=="New York","MANHATTAN",ifelse(CITY=="BROOKLYN","BROOKLYN",ifelse(CITY=="BRONX","BRONX","QUEENS"))))

To get a better understanding of the distribution of these licenses, I have provided a bar graph by borough.

ggplot(sidewalks, aes(x=fct_infreq(BOROUGH)))+geom_bar(aes(fill=BOROUGH))+ggtitle("Frequency of Sidewalk Cafe Licenses by Borough")+xlab("Borough")+ylab("Frequency")

Clearly Manhattan has the most license requests, followed by Brooklyn, then Queens and finally Bronx. Since at the moment we don’t have neighborhood information (everything in Manhattan is just classified as New York, Brooklyn has only Brooklyn, and Bronx has only the city of Bronx), we can only dive into the Queens data:

queens_cafes <- sidewalks %>% filter(BOROUGH=="QUEENS")
ggplot(queens_cafes, aes(x=fct_infreq(CITY)))+geom_bar(fill="purple")+ggtitle("Frequency of Sidewalk Cafe Licenses in Queens")+xlab("City / Neighborhood")+ylab("Frequency")+coord_flip()

In Queens, a large percentage of license requests come from Astoria, followed by Long Island City and Forest Hills.

Next, in order to do date comparisons to ascertain which are the new applications vs. renewal applications, I had to convert certain date fields from strings (they were read in as string factors) into dates.

sidewalks$EXPIRATION_DATE<-as.Date(sidewalks$EXPIRATION_DATE, format="%m/%d/%Y")
sidewalks$APP_STATUS_DATE<-as.Date(sidewalks$APP_STATUS_DATE, format="%m/%d/%Y")
sidewalks$SUBMIT_DATE<-as.Date(sidewalks$SUBMIT_DATE, format="%m/%d/%Y")
Licenses by Status

The list of licenses includes active licenses, expired licenses, licenses for businesses that have closed (and are now inactive), licenses which are up for renewal as part of the two year process, or new requests for licenses. To better classify them, I created a new field called STATUS_CLASSIFICATION. Those licenses which are still active and not up for renewal are classified as “ACTIVE”. Those licenses that have been submitted for renewal (either because their expiration date is less than the latest application data, or that an active license is up for review) are classified as “RENEWAL”. Those licenses that are in the sheet but do not have a license number are classified as “NEW”, and the rest are marked as “OLD” to encompass inactive licenses that have not been acted upon.

sidewalks<-sidewalks %>% mutate(STATUS_CLASSIFICATION = ifelse(LIC_STATUS=="Active" & (APP_STATUS=="Application Approved" | APP_STATUS=="Application Review Completed"),"ACTIVE",ifelse(is.na(LICENSE_NBR),"NEW",ifelse((APP_STATUS_DATE>EXPIRATION_DATE | DPQA=="Issued Temp Op Letter") | (LIC_STATUS=="Active" & (APP_STATUS=="Pending Review" | APP_STATUS=="Submitted")),"RENEWAL","OLD"))))

Now that we have classified the status of the licenses, we are able to see how these classifications differ between the boroughs.

ggplot(sidewalks)+geom_mosaic(aes(x=product(STATUS_CLASSIFICATION,BOROUGH),fill=factor(STATUS_CLASSIFICATION)))+coord_flip()+labs(x="Borough",y="License Status", fill="License Designation")+ggtitle("Boroughs by License Status")

The mosaic plot shows how Bronx and Brooklyn may be getting more new license requests as a percentage of total licenses. Bronx is also getting the highest percentage of renewal requests out of its inactive and active licenses. We can also take a look at the license designations by borough:

ggplot(sidewalks)+geom_mosaic(aes(x=product(BOROUGH,STATUS_CLASSIFICATION),fill=factor(BOROUGH)))+coord_flip()+labs(x="License Status",y="Borough", fill="Borough")+ggtitle("License Status by Borough")

Looking at the data in this way, you can see how Brooklyn has the second-most new license requests, but how Manhattan still dominates in all license status categories.

Mapping Licenses

We can map the data to have a better view of where the datapoints lie. To get an overall picture, I selected a map centered on Long Island City in Queens so that we can get a good view of both Brooklyn and Bronx in addition to Manhattan.

map <- get_map( location = c(-73.9485424, 40.7454513),  source = "google", zoom = 11, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=40.745451,-73.948542&zoom=11&size=640x640&scale=2&maptype=roadmap&language=en-EN

Plotting each of the restaurants colored by their borough. You can see how Manhattan dominates in the number of sidewalk cafes, and how the sidewalk cafes in Brooklyn and Queens are largely concentrated in the areas closer to Manhattan.

ggmap(map)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color=BOROUGH),data=sidewalks, alpha=0.3)+ggtitle("Distribution of all licenses (inactive or active) in NYC")

Even with alpha, it is difficult to tell exactly where the largest concentrations of sidewalk cafes lie. Using a density plot, we can better see the concentration of sidewalk cafes around mid-to lower Manhattan, and the clusters in Astoria and Williamsburg.

g <- ggmap(map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks, geom="polygon", alpha=0.2)
g+scale_fill_gradient(low="yellow",high="red")+ggtitle("Heatmap of Sidewalk Cafe Licenses in NYC")

The next question we can ask is whether there are clear patterns to where new license requests are coming in from, where they are being renewed, or where they have expired without renewal.

ggmap(map)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color=BOROUGH),data=sidewalks, alpha=0.4)+facet_wrap(~STATUS_CLASSIFICATION)+ggtitle("Sidewalk Cafe Licenses by Status")

However, since we are zoomed out a lot and looking at the entire set of licenses, it is difficult to tell the difference between the distributions of license requests. For this analysis, I am only concerned about active licenses (whether they are being renewed or not), and new requests that have not yet been approved. To do this, I created another STATUS_CLASSIFICATION that groups active renewal requests into the Active category, and sets all other non-new requests as “inactive”.

sidewalks <- sidewalks %>% mutate(STATUS_CLASSIFICATION2=ifelse(STATUS_CLASSIFICATION=="ACTIVE" | (STATUS_CLASSIFICATION=="RENEWAL" & LIC_STATUS=="Active"), "ACTIVE", ifelse(STATUS_CLASSIFICATION=="NEW","NEW","OLD")))
new_active <- sidewalks %>% filter(STATUS_CLASSIFICATION2=="ACTIVE" | STATUS_CLASSIFICATION2=="NEW")
ggmap(map)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color=BOROUGH),data=new_active, alpha=0.3)+facet_wrap(~STATUS_CLASSIFICATION2)+ggtitle("Active and New Licenses in New York City")

Since we are quite zoomed out, it is difficult to see what exactly is happening with the New requests. However, if we zoomed in, we would lose information about the Bronx or lower Brooklyn. In order to determine whether there is any useful information there, I have zoomed in on the Bronx region.

Geographic Analysis - Bronx
bronx_map <- get_map("Bronx, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=Bronx,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Bronx%2C%20NY
ggmap(bronx_map)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color=BOROUGH),data=new_active)+facet_wrap(~STATUS_CLASSIFICATION2)+ggtitle("Active and New Licenses in the Bronx")

Since there are very few active or new licenses in the Bronx, I feel comfortable in zooming in on the rest of Manhattan / Brooklyn and Queens in order to be able to better see what is happening at the expense of Bronx.

Geographic Analysis - Manhattan

I want to take a closer look at what is happening in Manhattan. In order to do this at a more granular level, I will use my merged data with our master_zip document which lists the neighborhoods of each Borough by zip code. I also pulled out the year of the submission of the license application and saved it as SUBMIT_YEAR.

master_zip<-read.csv("Data/zips_master_no_missing_nbrh.csv", strip.white=TRUE)
sidewalks_nbh <- merge(sidewalks, master_zip, by="ZIP", all.x=TRUE)
sidewalks_nbh <- sidewalks_nbh %>% mutate(SUBMIT_YEAR=year(SUBMIT_DATE))
sidewalks_manhattan_active <- sidewalks_nbh %>% filter(BOROUGH=="MANHATTAN" & (STATUS_CLASSIFICATION2=="ACTIVE" | STATUS_CLASSIFICATION2=="NEW"))
manhattan_map <- get_map("Manhattan, NY", source="google", maptype="roadmap", zoom=12, color="bw")
Source : https://maps.googleapis.com/maps/api/staticmap?center=Manhattan,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Manhattan%2C%20NY

I have also calculated the average locations of the neighborhoods in each borough based on their assigned Zip codes.

ggmap(manhattan_map)+geom_point(aes(x=LONGITUDE, y=LATITUDE, color=nbh), data=sidewalks_manhattan_active)+ggtitle("All active or new sidewalk cafes in Manhattan")

sidewalks_bronx<- sidewalks_nbh %>% filter(BOROUGH=="BRONX")

This view shows all of the distribution of all currently active or new sidewalk cafes in Manhattan. To get a better idea of density, we can also look at a heatmap of this view.

g <- ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan_active, geom="polygon", alpha=0.3)
g+scale_fill_gradient(low="yellow",high="red")+ggtitle("Heatmap of Sidewalk Cafe Licenses in Manhattan")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)

This heatmap shows the highest concentration of sidewalk cafes in Greenwich Village and NoHo. To see the difference between active and new licenses, we can facet based on our previously calculated categorization.

g <- ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan_active, geom="polygon", alpha=0.3)+facet_wrap(~STATUS_CLASSIFICATION2)
g+scale_fill_gradient(low="yellow",high="red")+ggtitle("Heatmap of Sidewalk Cafe Licenses in Manhattan")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)

The heatmap view of the map is not a great one - it is difficult to tell the differences on the same scales in a heatmap. Looking at the same view, but by using geom_point again, we get the following view:

ggmap(manhattan_map)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color=nbh),data=sidewalks_manhattan_active)+facet_wrap(~STATUS_CLASSIFICATION2)+ggtitle("Active and New Licenses in Manhattan") + guides(color=FALSE)+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)

Once again, the data is not telling us too much since there are very few new requests. Perhaps a better way to categorize this data is to look at the years that applications were submitted, therefore ignoring whether a license is currently active or not.

year_bxp<-ggplot(sidewalks_nbh, aes(y=SUBMIT_YEAR,x=1))+geom_boxplot()+ggtitle("Boxplot of Submission Years")
year_bar<-ggplot(sidewalks_nbh,aes(x=SUBMIT_YEAR))+geom_bar()+ggtitle("Bar Graph of Submission Years")
grid.arrange(year_bxp, year_bar, ncol=2)

Since there are very few submissions between 2000 and 2014, I will be removing these outliers as potential mistakes where the submit dates were not updated once the licenses were renewed. Now we can look at a mosaic plot of the different neighborhoods and the years that the licenses were requested.

sidewalks_nbh<- sidewalks_nbh %>% filter(SUBMIT_YEAR>2014)
sidewalks_manhattan<- sidewalks_nbh %>% filter(BOROUGH=="MANHATTAN")
manhattan_counts <- sidewalks_manhattan %>% group_by(nbh, SUBMIT_YEAR) %>% summarise(count=n())
manhattan_counts$SUBMIT_YEAR<-as.factor(manhattan_counts$SUBMIT_YEAR)
manhattan_counts$SUBMIT_YEAR<-factor(manhattan_counts$SUBMIT_YEAR, c("2017", "2016","2015"))
ggplot(manhattan_counts, aes(x=reorder(nbh, count), y=count, fill=SUBMIT_YEAR))+geom_bar(stat="identity",position=position_dodge())+coord_flip()+xlab("Neighborhood")+ggtitle("License Requests per Neighborhood and Year")

It makes sense that 2016 would have more applications than 2015, since many of the 2015 applications have been renewed by now. It looks like Greenwich Village, Upper West Side, and Upper Easy Side consistently have the highest number of requests throughout the three years. However, to get a better understanding of the distribution of areas, we can create three separate heatmaps.

map2015 <- ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan%>%filter(SUBMIT_YEAR=="2015"), geom="polygon", alpha=0.3)+scale_fill_gradient(low="yellow",high="red")+ggtitle("Manhattan Licenses Applied for in 2015")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)
map2015

map2016<-ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan%>%filter(SUBMIT_YEAR=="2016"), geom="polygon", alpha=0.3)+scale_fill_gradient(low="yellow",high="red")+ggtitle("Manhattan Licenses Applied for in 2016")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)
map2016

map2017 <- ggmap(manhattan_map)+stat_density2d(aes(x=LONGITUDE,y=LATITUDE, fill=..level..),data=sidewalks_manhattan%>%filter(SUBMIT_YEAR=="2017"), geom="polygon", alpha=0.3)+scale_fill_gradient(low="yellow",high="red")+ggtitle("Manhattan Licenses Applied for in 2017")+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)
map2017

Across all three years, you can see how the concentrated area of licenses remains in the Greenwich Village area. However, it looks like the concentration of applicants in the Upper West Side in 2017 has dropped. This map is more useful than the bar chart because it shows the location of the cafes that may be straddling two neighborhoods - information that is not apparent from the grouped bar chart.

We can do the same sort of analysis in all four boroughs that we have sidewalk cafe information about. To make this analysis easier to view, I have created a shiny app which can be found by following this link: ShinyCafe

sidewalks_brooklyn <- sidewalks_nbh %>% filter(BOROUGH=="BROOKLYN")
sidewalks_queens <- sidewalks_nbh %>% filter(BOROUGH=="QUEENS")
sidewalks_bronx <- sidewalks_nbh %>% filter(BOROUGH=="BRONX")
brooklyn_map <- get_map("Brooklyn, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=Brooklyn,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Brooklyn%2C%20NY
queens_map <- get_map("Astoria, NY",  source = "google", zoom = 12, maptype="roadmap", color="bw") 
Source : https://maps.googleapis.com/maps/api/staticmap?center=Astoria,+NY&zoom=12&size=640x640&scale=2&maptype=roadmap&language=en-EN
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Astoria%2C%20NY

The Shiny app has a brief blurb explaining what each borough can tell us about the movement of sidewalk cafe applications.

Next, I added Adam’s income distribution ploygons to the background of these maps to see what the relationship is to income.

ggmap(manhattan_map)+geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_manhattan, alpha = 0.7)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color="purple"),data=sidewalks_manhattan, size=1)+ scale_fill_viridis()+geom_text_repel(data=manhattan_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)+guides(color=F)

Manhattan has a pretty clear clustering of sidewalk cafes in the higher income areas, with the exception of Lower Mahattan and the Financial District.

ggmap(brooklyn_map)+geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_brooklyn, alpha = 0.7)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color="purple"),data=sidewalks_brooklyn, size=1)+ scale_fill_viridis()+guides(color=FALSE)

The missing data in Williamsburg is hindering this analysis, but we do see how the average income in Brooklyn Heights, Park Slope, and Red Hook correlate with more sidewalk cafes.

ggmap(queens_map)+geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_queens, alpha = 0.7)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color="purple"),data=sidewalks_queens, size=1)+ scale_fill_viridis()+geom_text_repel(data=queens_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)+guides(color=F)

Astoria and Long Island City are interesting cases with relatively low average incomes but a high concentration of sidewalk cafes. The other area to the south-east, around Parkside and Forest Hills, has a high concentration of cafes and high income.

ggmap(bronx_map)+geom_polygon(aes(fill = income., x = long, y = lat, group = group), data = plotting_zips_bronx, alpha = 0.7)+geom_point(aes(x=LONGITUDE,y=LATITUDE, color="purple"),data=sidewalks_bronx, size=1)+ scale_fill_viridis()+geom_text_repel(data=bronx_names, aes(mean_lon, mean_lat, label=Neighborhood),fontface="bold", size=3)+guides(color=F)

With the very low number of Bronx datapoints, it is not easy to see a realtionship between income and where the sidewalk cafes are located.

summary_cafe <- merge(zip_sidewalk, mean_incomes, by=ZIP)
Error in fix.by(by.x, x) : object 'ZIP' not found

cafe.lm = lm(mean_income~n_cafes, data=summary_cafe) 
summary(cafe.lm)$r.squared
[1] 0.1965228

On all the data, there is pretty weak correlation between income and number of sidewalk cafes, with an r-squares of 0.196.

Faceting by Borough, we can see how the relationship is actually negative in Bronx and Queens, but positive in Manhattan and Brooklyn (with the strongest relationship in Manhattan).

Liquor License Data - Rohan

6. Conclusion - Rohan

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCiMjIDEuIEludHJvZHVjdGlvbg0KDQpPbmUgb2Ygb3VyIHRlYW1tYXRlcyBsaXZlcyBpbiBIYW1pbHRvbiBIZWlnaHRzLCBpbiBhbiAndXAgYW5kIGNvbWluZycgYXJlYSBjYWxsZWQgU3VnYXIgSGlsbCB3aGljaCBoYXMgc2VlbiBhIGxvdCBvZiBuZXcgYmFycyBhbmQgcmVzdGF1cmFudHMgb3BlbiB1cCAod2UgaGlnaGx5IHN1Z2dlc3QgY2hlY2tpbmcgb3V0IEhhcmxlbSBQdWJsaWMpLiBXZSB3ZXJlIHdvbmRlcmluZyBob3cgb25lIGNvdWxkIGVhc2lseSBpZGVudGlmeSBhbiAndXAgYW5kIGNvbWluZycgYXJlYSBiZWZvcmUgYWxsIHRoZSBsYW5kIHZhbHVlcyByaXNlLiBDb3VsZCB0aGV5IGJlIGlkZW50aWZpZWQgYnkgbmV3IHdpbmViYXJzIGFuZCBwdWJzPyBPciBwZXJoYXBzIGJ5IHNpZGV3YWxrIGNhZmVzIHRoYXQgcG9wIHVwIGFzIHNvb24gYXMgdGhlIHdlYXRoZXIgZ2V0cyB3YXJtZXI/ICANCg0KV2UgZm91bmQgYSBkYXRhc2V0IGZvciBsaXF1b3IgbGljZW5zZXMgaW4gTmV3IFlvcmsgU3RhdGUgYXQgZGF0YS5ueS5nb3YuIENhbGxlZCBbIkxpcXVvciBBdXRob3JpdHkgUXVhcnRlcmx5IExpc3Qgb2YgQWN0aXZlIExpY2Vuc2VzIl0oaHR0cHM6Ly9kYXRhLm55Lmdvdi9FY29ub21pYy1EZXZlbG9wbWVudC9MaXF1b3ItQXV0aG9yaXR5LVF1YXJ0ZXJseS1MaXN0LW9mLUFjdGl2ZS1MaWNlbnNlcy9ocnZzLWZ4czIpLCB0aGlzIGRhdGFzZXQgZ2l2ZXMgaW5mb3JtYXRpb24gYWJvdXQgYWxsIG9mIHRoZSBjdXJyZW50bHkgYWN0aXZlIGxpcXVvciBsaWNlbnNlcyBhcyBvZiBBcHJpbCwgMjAxNy4NCg0KV2UgYWxzbyBmb3VuZCBhIGRhdGFzZXQgYWJvdXQgYWxsIHJlcXVlc3RlZCBhbmQgYWN0aXZlIHNpZGV3YWxrIGNhZmUgbGljZW5zZSBhcHBsaWNhdGlvbnMgaW4gTmV3IFlvcmsgQ2l0eSAoYW5kIHRoZSBzdXJyb3VuZGluZyBib3JvdWdocyBleGNsdWRpbmcgTG9uZyBJc2xhbmQpIGNhbGxlZCBbIlNpZGV3YWxrIENhZukgTGljZW5zZXMgYW5kIEFwcGxpY2F0aW9ucyJdKGh0dHBzOi8vZGF0YS5jaXR5b2ZuZXd5b3JrLnVzL0J1c2luZXNzL1NpZGV3YWxrLUNhZi1MaWNlbnNlcy1hbmQtQXBwbGljYXRpb25zL3FjZGotcndodSkuIFRoaXMgaGFzIGFsc28gYmVlbiB1cGRhdGVkIGluIEFwcmlsLCAyMDE3Lg0KDQpGaW5hbGx5LCB3ZSB3YW50ZWQgdG8ga25vdyB3aGV0aGVyIHRoZXJlIGlzIGEgY2xlYXIgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYXZlcmFnZSBpbmNvbWUgb2YgbmVpZ2hib3Job29kIGluaGFiaXRhbnRzIGFuZCB0aGUgbnVtYmVyIG9mIHNpZGV3YWxrIGNhZmVzIGFuZCBiYXJzIGluIHRoYXQgYXJlYS4gVG8gZG8gdGhpcywgd2UgdXNlZCB0aGUgZm9sbG93aW5nIGRhdGFzZXRzOiAgIA0KDQoqIFtJbmNvbWUgVGllZCB0byBaaXBjb2RlXShodHRwczovL2dpdGh1Yi5jb20vamNoZW5nNS9zdXBlcnppcC90cmVlL21hc3Rlci9kYXRhKSAgDQoNCiogW01hcHBpbmcgb2YgWmlwQ29kZXMgdG8gTmVpZ2hib3Job29kcyBhbmQgTG9uL0xhdCBkYXRhXShodHRwczovL3d3dy5iYXJ1Y2guY3VueS5lZHUvY29uZmx1ZW5jZS9kaXNwbGF5L2dlb3BvcnRhbC9OWUMrR2VvZ3JhcGhpZXMpICANCg0KKiBbWmlwY29kZSBTaGFwZWZpbGVzXShodHRwczovL2RhdGEuY2l0eW9mbmV3eW9yay51cy9CdXNpbmVzcy9aaXAtQ29kZS1Cb3VuZGFyaWVzL2k4aXcteGY0dSkgIA0KDQoNCldlIHJlYWxpemVkIHRoYXQgdGhlcmUgaXMgbm8gYXV0aG9yaXRhdGl2ZSBkYXRhc291cmNlIG1hcHBpbmcgZWFjaCB6aXAgY29kZSB0byBhIHNpbmdsZSBuZWlnaGJvcmhvb2QuIEluIG9yZGVyIHRvIGJlIGFibGUgdG8gbWFwIG5laWdoYm9yaG9vZCBsb2NhdGlvbnMgb24gYSBtYXAsIHdlIHdyb3RlIGEgcHl0aG9uIHNjcmFwZXIgdG8gcHVsbCB6aXBjb2RlIHRvIG5laWdoYm9yaG9vZCBkYXRhIGZyb20gdGhlIGZvbGxvd2luZyBsb2NhdGlvbnMsIGFuZCB0aGVuIGpvaW5lZCBpdCB3aXRoIHRoZSB6aXBjb2RlIHRvIGxvY2F0aW9uIGRhdGE6ICANCg0KKiBbTWFuaGF0dGFuIE5laWdoYm9yaG9vZHNdKGh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vbWFwcy9kL3UvMC92aWV3ZXI/bWlkPTFQNkNoZHlaZERrQzJOM1g0YmlFRTB5ZzVkOTAmaGw9ZW5fVVMmbGw9NDAuNzg2OTE0Nzg5NzEyODIlMkMtNzMuOTY1NjM4NDg0NjE5MTUmej0xMikgIA0KDQoqIFtCcm9va2x5biBOZWlnaGJvcmhvb2RzXShodHRwOi8vd3d3LmJyb29rbHluLmNvbS96aXBjb2Rlcy5waHApICANCiANCiogW1F1ZWVucyBOZWlnaGJvcmhvb2RzXShodHRwOi8vcXVlZW5zLmFib3V0LmNvbS9vZC9uZWlnaGJvcmhvb2RzL2EvemlwLWNvZGVzLXF1ZWVucy1ueS5odG0pICANCg0KKiBbQnJvbnggTmVpZ2hib3Job29kc10oaHR0cDovL3N0YXRpc3RpY2FsYXRsYXMuY29tL2NvdW50eS1zdWJkaXZpc2lvbi9OZXctWW9yay9Ccm9ueC1Db3VudHkvQnJvbngvT3ZlcnZpZXcpDQoNCiMjIDIuIFRlYW0gLSBBZGFtDQoNCkxpc3QgdGVhbSBtZW1iZXJzIGFuZCBhIGRlc2NyaXB0aW9uIG9mIGhvdyBlYWNoIGNvbnRyaWJ1dGVkIHRvIHRoZSBwcm9qZWN0LiAoSWYgeW91J3JlIHdvcmtpbmcgYWxvbmUsIGJyaWVmbHkgZGVzY3JpYmUgdGhlIHN0YWdlcyBvZiB0aGUgcHJvamVjdC4pIA0KDQoxLiBBZGFtIENvdmllbnNreSAtICANCkFkYW0gd2FzIG1vc3RseSBpbiBjaGFyZ2Ugb2YgdGhlIGluY29tZSBwZXIgemlwY29kZSBkYXRhLiBIZSBmb3VuZCBhbmQgYW5hbHl6ZWQgdGhlIG9yaWdpbmFsIHByb3BlcnR5IHZhbHVlIGRhdGFzZXQgd2hpY2ggd2UgZGlkIG5vdCBlbmQgdXAgdXNpbmcgb25jZSB3ZSBkZXRlcm1pbmVkIHRoYXQgaXQgd291bGQgYmUgaGFyZCB0byBtYXAgdGhlIG5laWdoYm9yaG9vZHMgYW5kIGNhZmUgYW5kIGxpcXVvciBsaWNlbnNlIGRhdGEgd2l0aG91dCB6aXBjb2Rlcy4gSGUgZm91bmQgYW5kIGNsZWFuZWQgbmV3IHN1cGVyemlwIGRhdGFzZXQgYW5kIGhlbHBlZCBqb2luIHRoZSB6aXBjb2RlcyB0byBuZWlnaGJvcmhvb2RzLiBIZSBhbHNvIGRpZCB0aGUgemlwY29kZSBzaGFwZWZpbGUgZ3JhcGhzIGJ5IGluY29tZSBhbmQgd3JvdGUgdGhlIHdyaXRldXAgY29ycmVzcG9uZGluZyB0byBoaXMgc2VjdGlvbnMuIEhlIGhlbHBlZCB0byBjb21iaW5lIHRoZSBhbmFseXNpcyBhbmQgbWFrZSBvYnNlcnZhdGlvbnMuDQoNCg0KMi4gUm9oYW4gUGl0cmUgLSAgDQoNCjMuIE1hcmlrYSBMb2htdXMgLSAgDQoNCmBgYHtyfQ0KDQpyZXF1aXJlKGdncGxvdDIpDQpyZXF1aXJlKGRwbHlyKQ0KcmVxdWlyZSh0aWR5cikNCnJlcXVpcmUoZXh0cmFjYXQpDQpyZXF1aXJlKGZvcmNhdHMpDQpyZXF1aXJlKGdnbW9zYWljKQ0KcmVxdWlyZShnZ21hcCkNCnJlcXVpcmUoZ3JpZEV4dHJhKQ0KcmVxdWlyZShnZ3JlcGVsKQ0KcmVxdWlyZShsdWJyaWRhdGUpDQpyZXF1aXJlKHNoaW55KQ0KcmVxdWlyZSh6aXBjb2RlKQ0KcmVxdWlyZSh2aXJpZGlzKQ0KcmVxdWlyZShyZ2RhbCkNCnJlcXVpcmUobWFwdG9vbHMpDQpgYGANCg0KIyMgMy4gQW5hbHlzaXMgb2YgRGF0YSBRdWFsaXR5IC0gQWxsDQoNCiMjIyMgWmlwIENvZGUsIE5laWdoYm9yaG9vZCBhbmQgTGFuZCBWYWx1ZSBEYXRhIC0gQWRhbQ0KDQpXZSByZWFsaXplZCB0aGF0IHRoZSBvcmlnaW5hbCBkYXRhIHdhcyBub3QgZ29pbmcgdG8gYmUgc3VmZmljaWVudC4gV2Ugb25seSBoYWQgYSBmZXcgcmVudGFsIG9ic2VydmF0aW9ucyBmcm9tIFN0YXRlbiBJc2xhbmQsIFF1ZWVucyBhbmQgdGhlIEJyb254LCBhbmQgYWxsIG9mIHRoZSBvYnNlcnZhdGlvbnMgd2VyZSBkaXJlY3RseSBib3JkZXJpbmcgTWFuaGF0dGFuLiBXZSBkZWNpZGVkIHRvIGZpbmQgbmV3IGRhdGEuIFdlIGZvdW5kIHRoZSBkYXRhIGZvciBhIFNoaW55IHByb2plY3QgdGl0bGVkIFN1cGVyemlwIHdpdGggSW5jb21lIHBlciB6aXBjb2RlIGZvciB0aGUgZW50aXJlIFVuaXRlZCBTdGF0ZXMuIFNpbmNlIHdlIGFsc28gd2FudGVkIHRoZSBpbmNvbWUgcGVyIG5laWdoYm9yaG9vZCwgd2UgZm91bmQgYSBtYXBwaW5nIGZyb20gemlwY29kZSB0byBuZWlnaGJvcmhvb2QgbmFtZSB0aGF0IHdlIHdpbGwgdXNlIGZvciBvdXIgZmluYWwgb2JzZXJ2YXRpb25zLiBJdCBwcm92ZWQgcmF0aGVyIGRpZmZpY3VsdCB0byBhY3R1YWxseSBjbGVhbiB0aGlzIG1hcHBpbmcuDQoNCmBgYHtyfQ0Kc3VwZXJ6aXAgPC0gcmVhZC50YWJsZSgnRGF0YS9zdXBlcnppcC5jc3YnLCBoZWFkZXIgPSBUUlVFKQ0KbmVpZ2hib3Job29kIDwtIHJlYWQuY3N2KCdEYXRhL255Y196Y3RhLmNzdicpDQpjb2xuYW1lcyhuZWlnaGJvcmhvb2QpWzFdIDwtICJ6aXBjb2RlIg0KbmVpZ2hib3Job29kMiA8LSBuZWlnaGJvcmhvb2RbLGMoMSwgNildDQpuZWlnaGJvcmhvb2QyJHppcGNvZGUgPC0gYXMuZmFjdG9yKG5laWdoYm9yaG9vZDIkemlwY29kZSkNCmBgYA0KDQoNCmBgYHtyfQ0Kc3VwZXJ6aXAyIDwtIHN1cGVyemlwICU+JSBkcGx5cjo6ZmlsdGVyKHN0YXRlID09ICJOWSIpDQpzdXBlcnppcDMgPC0gc3VwZXJ6aXAyICU+JSBmaWx0ZXIoY2l0eSAlaW4lIGMoIkJyb29rbHluIiwgIlF1ZWVucyBWaWxsYWdlIiwgIlN0YXRlbiBJc2xhbmQiLCAiTmV3IFlvcmsiLCAiQnJvbngiKSkNCnN1cGVyemlwMyRjaXR5W3N1cGVyemlwMyRjaXR5PT0iTmV3IFlvcmsiXSA9ICJNYW5oYXR0YW4iDQpgYGANCg0KV2l0aG91dCBmaWx0ZXJpbmcgZm9yIHRoZSBzdGF0ZSA9PSBOWSwgd2UgZm91bmQgd2UgaGFkIHNvbWUgZXh0cmEgcm93cy4gV2hlbiB3ZSBsb29rZWQgdXAgdGhlc2UgemlwY29kZXMgb25saW5lLCB3ZSBmb3VuZCB0aGF0IHRoZXJlIHdlcmUgNiBvdGhlciBCcm9va2x5bnMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuIFRodXMsIHdlIGhhZCB0byBmaXJzdCBmaWx0ZXIgZm9yIE5ZLCB0aGVuIG9ubHkgY29udGFpbmluZyB0aGUgYm9yb3VnaHMuIFRoZW4sIHdlIGNoYW5nZWQgdGhlIGJvcm91Z2ggZnJvbSBOZXcgWW9yayB0byBiZSBNYW5oYXR0YW4NCg0KDQpgYGB7cn0NCm1hc3Rlcl96aXAgPC0gZnVsbF9qb2luKHN1cGVyemlwMywgbmVpZ2hib3Job29kMiwgYnkgPSAnemlwY29kZScpDQptYXN0ZXJfemlwWyFjb21wbGV0ZS5jYXNlcyhtYXN0ZXJfemlwKSxdDQpjb2xuYW1lcyhtYXN0ZXJfemlwKVsxMV0gPC0gImFyZWEiDQpgYGANCg0KVGhpcyBzaG93cyB1cyB0aGUgcm93cyB3aGljaCBoYXZlIE5Bcy4gQXMgd2UgY2FuIGNsZWFybHkgc2VlLCB0aGVyZSB3ZXJlIG1vcmUgemlwY29kZXMgaW4gdGhlIG5laWdoYm9yaG9vZCBkYXRhIHRoYW4gaW4gdGhlIHN1cGVyemlwIGRhdGEuIE91ciB3YXkgb2YgYWRqdXN0aW5nIGZvciB0aGlzIHdpbGwgYmUgdG8gZGV0ZXJtaW5lIGFuIGF2ZXJhZ2UgaW5jb21lIGZvciBlYWNoIG5laWdoYm9yaG9vZC4gVGhlbiwgd2Ugd2lsbCBiZSBpbXB1dGluZyB0aGUgbWlzc2luZyBpbmNvbWVzIHVzaW5nIHRoZSBhdmVyYWdlIGluY29tZXMgZm9yIHRoZSB6aXBjb2RlcyB3aGljaCB3ZSBkbyBoYXZlIGluIHRoYXQgbmVpZ2hib3Job29kLg0KDQoNCmBgYHtyfQ0KQVZHX0lOQ09NRV9ORUlHSEJPUkhPT0QgPC0gbWFzdGVyX3ppcCAlPiUgZ3JvdXBfYnkoYXJlYSkgJT4lIHN1bW1hcmlzZShhdmcgPSBtZWFuKGluY29tZS4sIG5hLnJtPVRSVUUpKQ0KYGBgDQoNClVwb24gdmlld2luZyB0aGUgb3V0cHV0LCB3ZSBzdGlsbCBoYXZlIG1hbnkgTkFzIGluIHRoZSBhdmcgY29sdW1uLCB3aGVuIGxvb2tpbmcgYXQgdGhlIG1hc3Rlcl96aXAgZGF0YWZyYW1lIGZpbHRlcmVkIGZvciAiQXN0b3JpYSAmIExvbmcgSXNsYW5kIENpdHkiLCB3aGljaCBpcyB0aGUgZmlyc3QgbmVpZ2hib3Job29kIHdpdGggYW4gYXZnIG9mIE5BLCB3ZSBzZWUgdGhhdCBpdCdzIGJlY2F1c2Ugd2UgaGF2ZSBubyBpbmNvbWUgZGF0YSBmb3IgYW55IHppcGNvZGVzIGluIHRoaXMgYXJlYS4gVGhlcmVmb3JlLCBvbmNlIHdlIGpvaW4gdGhlIGF2ZXJhZ2UgaW5jb21lIHdpdGggdGhlIG1hc3Rlcl96aXAgZGF0YWZyYW1lIHdlIHdpbGwgZHJvcCB0aGVzZSByb3dzIHdpdGggTkFzLg0KDQpgYGB7cn0NCm1hc3Rlcl96aXAyIDwtIGxlZnRfam9pbihtYXN0ZXJfemlwLCBBVkdfSU5DT01FX05FSUdIQk9SSE9PRCwgYnkgPSAiYXJlYSIpDQpgYGANCg0KSGVyZSB3ZSBoYXZlIGpvaW5lZCB0aGUgYXZlcmFnZSBpbmNvbWUgcGVyIG5laWdoYm9yaG9vZCBkYXRhIGJhY2sgdG8gdGhlIHJlc3Qgb2YgdGhlIGluY29tZSBkYXRhLiANCg0KYGBge3J9DQpkYXRhKHppcGNvZGUpDQpjZW5zdXMgPC0gbWVyZ2UobWFzdGVyX3ppcDIsIHppcGNvZGUsIGJ5LnggPSAnemlwY29kZScsIGJ5LnkgPSAnemlwJykNCmNlbnN1cyA8LSBjZW5zdXMgJT4lIGZpbHRlcighaXMubmEoYXZnKSkNCmBgYA0KDQpIZXJlIHdlIGFyZSB1c2luZyB0aGUgcGFja2FnZSB6aXBjb2RlIHRvIG1hcCBhbGwgb2Ygb3VyIHppcGNvZGVzIHRvIGdlb3NwYXRpYWwgY29vcmRpbmF0ZXMgc28gdGhhdCB3ZSBjYW4gcGxvdCB0aGVtIHVzaW5nIGdnbWFwLiBUaGVuLCB3ZSBhcmUgZmlsdGVyaW5nIG91dCBhbGwgb2YgdGhlIHppcGNvZGVzIGFuZCBuZWlnaGJvcmhvb2RzIGZvciB3aGljaCB3ZSBoYXZlIG5vIGluY29tZSBkYXRhIGF0IGFsbCB3aXRoaW4gdGhhdCBuZWlnaGJvcmhvb2QuDQoNCmBgYHtyfQ0KbWlzc2luZ196aXBzIDwtIGMoMTAwNjUsIDEwMDc1LCAxMDEwNiwgMTAyODEsIDExMTAxLCAxMTEwMiwgMTExMDMsIDExMTA0LCAxMTEwNSwgMTExMDYsIDExMTA5LCAxMTI0OSwgMTEzNjYsIDExMzY3LCAxMTM2OCwgMTEzNzAsIDExMzcyLCAxMTM3MywgMTEzNzQsIDExMzc1LCAxMTM3NywgMTEzNzksIDExNDM1LCAxMTY5NCkNCmZpbHRlcmVkX21pc3NpbmdfemlwcyA8LSBmaWx0ZXIoc3VwZXJ6aXAsIHppcGNvZGUgJWluJSBtaXNzaW5nX3ppcHMpDQpzZXRkaWZmKG1pc3NpbmdfemlwcywgZmlsdGVyZWRfbWlzc2luZ196aXBzJHppcGNvZGUpDQpgYGANCg0KSGVyZSwgTWFyaWthIGhhZCBpbmZvcm1lZCBtZSB0aGF0IHdlIGhhZCBtaXNzaW5nIHppcGNvZGVzIGluIHRoZSBmaW5hbCBjZW5zdXMgZGF0YWZyYW1lLiBUaGVyZWZvcmUsIEkgd2VudCBiYWNrIHRvIHRoZSBzdXBlcnppcCBkYXRhc2V0IGFuZCBmb3VuZCB0aGF0IDE4IG9mIHRoZSAyNCB6aXBjb2RlcyBhcHBlYXJlZCB0aGVyZS4gSG93ZXZlciwgdGhleSB3ZXJlIGxhYmVsZWQgYXMgYmVpbmcgaW4gZGlmZmVyZW50IGNpdGllcywgbm90IG9uZSBvZiB0aGUgZml2ZSBib3JvdWdocy4gU2luY2Ugc2hlIGhhcyBkYXRhIGluIHRoZXNlIHppcGNvZGVzLCB3ZSB3aWxsIGJlIGFkZGluZyB0aGVtIGJhY2sgaW50byB0aGUgY2Vuc3VzIGRhdGFmcmFtZS4gVGhlIDE4IHppcGNvZGVzIHdoaWNoIHdlIGhhZCBkYXRhIG9uIGNvcnJlc3BvbmRlZCBtb3N0bHkgdG8gZGF0YSBpbiBRdWVlbnMgd2hpY2ggd2VyZSBub3QgbGFiZWxsZWQgYXMgUXVlZW5zLiBUaGlzIGFsc28gZXhwbGFpbnMgdGhlIGxhY2sgb2YgYW55IGluY29tZSBkYXRhIGluIHNvbWUgb2YgdGhlIG5laWdoYm9yaG9vZHMuDQoNCldlIGFsc28gZm91bmQgdGhhdCA2IHppcGNvZGVzIGFyZSBzdGlsbCBtaXNzaW5nIGZyb20gb3VyIGluY29tZSBkYXRhLiBUaGVzZSBhcmUgMTAwNjUgKFVwcGVyIEVhc3QgU2lkZSksIDEwMDc1KFVwcGVyIEVhc3QgU2lkZSksIDEwMTA2KG1pZHRvd24pLCAxMDI4MSAoV29ybGQgVHJhZGUpLCAxMTEwOSAoTG9uZyBJc2xhbmQpIGFuZCAxMTI0OShXaWxsaWFtc2J1cmcpLg0KDQpgYGB7cn0NCnN1cGVyemlwNCA8LSByYmluZChzdXBlcnppcDMsIGZpbHRlcmVkX21pc3NpbmdfemlwcykNCm1hc3Rlcl96aXBfYWRkZWQgPC0gZnVsbF9qb2luKHN1cGVyemlwNCwgbmVpZ2hib3Job29kMiwgYnkgPSAnemlwY29kZScpDQptYXN0ZXJfemlwX2FkZGVkWyFjb21wbGV0ZS5jYXNlcyhtYXN0ZXJfemlwX2FkZGVkKSxdDQpjb2xuYW1lcyhtYXN0ZXJfemlwX2FkZGVkKVsxMV0gPC0gImFyZWEiDQpBVkdfSU5DT01FX05FSUdIQk9SSE9PRF9BRERFRCA8LSBtYXN0ZXJfemlwX2FkZGVkICU+JSBncm91cF9ieShhcmVhKSAlPiUgc3VtbWFyaXNlKGF2ZyA9IG1lYW4oaW5jb21lLiwgbmEucm09VFJVRSkpDQptYXN0ZXJfemlwMl9hZGRlZCA8LSBsZWZ0X2pvaW4obWFzdGVyX3ppcF9hZGRlZCwgQVZHX0lOQ09NRV9ORUlHSEJPUkhPT0RfQURERUQsIGJ5ID0gImFyZWEiKQ0KY2Vuc3VzX2FkZGVkIDwtIG1lcmdlKG1hc3Rlcl96aXAyX2FkZGVkLCB6aXBjb2RlLCBieS54ID0gJ3ppcGNvZGUnLCBieS55ID0gJ3ppcCcpDQpjZW5zdXNfYWRkZWQyIDwtIGNlbnN1c19hZGRlZCAlPiUgZmlsdGVyKCFpcy5uYShhdmcpKQ0Kd3JpdGUuY3N2KGNlbnN1c19hZGRlZDIsIGZpbGUgPSAiemlwX21hc3Rlcl9ub19taXNzaW5nLmNzdiIpDQpgYGANCg0KSGVyZSBJIHdlbnQgYmFjayBhbmQgYWRkZWQgaW4gdGhlIG1pc3NpbmcgemlwIGNvZGVzLiBJIHRoZW4gaGFkIHRvIHJlY29tcHV0ZSB0aGUgYXZlcmFnZSBzdGF0aXN0aWMgYWNjb3VudGluZyBmb3IgdGhlIG5ldyB6aXBjb2Rlcy4gSSByZXBlYXRlZCB0aGUgc2FtZSBwcm9jZXNzIGFzIGJlZm9yZSBvbmNlIEkgYWRkZWQgdGhlIHppcGNvZGVzIGJhY2sgaW4uDQoNCk5vdyB3ZSBhcmUgbG9hZGluZyBiYWNrIGluIHRoZSBnb29kIGRhdGEgYWZ0ZXIgd3JpdGluZyBzb21lIHRvIHRoZSBmb2xkZXIuDQpgYGB7cn0NCmNlbnN1cyA8LSByZWFkLmNzdigiRGF0YS96aXBzX21hc3Rlcl9ub19taXNzaW5nX25icmguY3N2IikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGNlbnN1cywgYWVzKHJlb3JkZXIoY2l0eS54LCAtYXZnLCBGVU4gPSBtZWRpYW4pLCBhdmcpKSArIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFRSVUUpICsgZ2d0aXRsZSgiQm94cGxvdHMgb2YgSW5jb21lIGJ5IEJvcm91Z2giKSArIGxhYnMoeD0iQm9yb3VnaCIsIHk9IkF2ZXJhZ2UgSW5jb21lIHBlciBuZWlnaGJvcmhvb2QiKQ0KYGBgDQoNCkhlcmUgd2Ugc2VlIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBhdmVyYWdlIGluY29tZSBwZXIgYXJlYSBpbiBlYWNoIG9mIHRoZSA1IGJvcm91Z2hzLiBUaGlzIHBsb3QgYXBwZWFycyB0byBzaG93IHRoYXQgUXVlZW5zIFZpbGxnZSBkb2VzIG5vdCBoYXZlIHZlcnkgbXVjaCBkYXRhIGluIGl0IGFzIHRoaXMgaXMgYSB2YXJpYWJsZSB3aWR0aCBib3hwbG90LiBIb3dldmVyLCBpdCB0dXJucyBvdXQgaXQgaXMgYmVjYXVzZSBhbGwgb2YgdGhlIG90aGVyIHNpbmdsZSBwb2ludCBuZWlnaGJvcmhvb2RzIGFyZSBhY3R1YWxseSBhIHBhcnQgb2YgUXVlZW5zLiBUaGlzIGlzIGEgcHJvYmxlbSB3ZSBoYWQgdG8gZml4LiBUaGVzZSBhcmUgdGhlIDE4IHppcGNvZGVzIHdlIGFkZGVkIGJhY2sgaW50byB0aGUgZGF0YS4NCg0KYGBge3J9DQpjZW5zdXMkY2l0eS54WyEoY2Vuc3VzJGNpdHkueSAlaW4lIGMoIkJyb29rbHluIiwgIk5ldyBZb3JrIiwgIkJyb254IiwgIlN0YXRlbiBJc2xhbmQiKSldIDwtICJRdWVlbnMgVmlsbGFnZSINCmNlbnN1cyRjaXR5LnlbIShjZW5zdXMkY2l0eS55ICVpbiUgYygiQnJvb2tseW4iLCAiTmV3IFlvcmsiLCAiQnJvbngiLCAiU3RhdGVuIElzbGFuZCIpKV0gPC0gIlF1ZWVucyBWaWxsYWdlIg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGNlbnN1cywgYWVzKHJlb3JkZXIoY2l0eS54LCAtYXZnLCBGVU4gPSBtZWRpYW4pLCBhdmcpKSArIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFRSVUUpICsgZ2d0aXRsZSgiQm94cGxvdHMgb2YgSW5jb21lIGJ5IEJvcm91Z2giKSArIGxhYnMoeD0iQm9yb3VnaCIsIHk9IkF2ZXJhZ2UgSW5jb21lIHBlciBuZWlnaGJvcmhvb2QiKQ0KYGBgDQoNCg0KQWxzbywgdGhlcmUgaXMgdmVyeSBsaXR0bGUgc3ByZWFkIGluIHRoZSBkaXN0cmlidXRpb24gZm9yIEJyb254IGFuZCBCcm9va2x5biBjb21wYXJlZCB0byBNYW5oYXR0YW4sIHdoaWNoIGhhcyBhIG1hc3NpdmUgc3ByZWFkLiBUaGUgbWlzc2luZyB2YWx1ZXMgb2NjdXIgZnJvbSB6aXBjb2RlcyB3aGljaCBvY2N1ciBpbiB0aGUgbmVpZ2hib3Job29kIGRhdGEgYW5kIG5vdCBpbiB0aGUgc3VwZXJ6aXAgZGF0YS4gV2UgY2FuIHNlZSB0aGF0IHRoZSBhdmVyYWdlIGluY29tZSBjYWxjdWxhdGVkIGZvciB0aGVzZSB6aXBjb2RlcyBpbiBnZW5lcmFsIGFyZSBoaWdoLiBXZSBjYW4gdGh1cyBpbmZlciB0aGV5IGFyZSBsaWtlbHkgZnJvbSBOZXcgWW9yayBhbmQgbG9va2luZyBiYWNrIGF0IHRoZSBjZW5zdXMgZGF0YSBhbmQgZmlsdGVyaW5nIGZvciB0aGUgTkFzLCB3aGljaCB3YXMgaW5kZWVkIHRoZSBjYXNlLg0KDQpgYGB7cn0NCmdncGxvdChjZW5zdXMsIGFlcyhyZW9yZGVyKGNpdHkueSwgLWF2ZywgRlVOID0gbWVkaWFuKSwgYXZnKSkgKyBnZW9tX2JveHBsb3QodmFyd2lkdGggPSBUUlVFKSArIGdndGl0bGUoIkJveHBsb3RzIG9mIEluY29tZSBieSBCb3JvdWdoIikgKyBsYWJzKHg9IkJvcm91Z2giLCB5PSJBdmVyYWdlIEluY29tZSBwZXIgbmVpZ2hib3Job29kIikNCmBgYA0KDQpIZXJlIHdlIHNlZSB3aXRoIHRoZSBwbG90IHdpdGggdGhlIG51bGwgdmFsdWVzIHJlbW92ZWQuDQoNCmBgYHtyfQ0KY2Vuc3VzICU+JSBmaWx0ZXIoIWlzLm5hKGNlbnN1cyRjaXR5LngpKSAlPiUgZ2dwbG90KGFlcyhjaXR5LngsIGluY29tZS4sIGNvbG9yPWNpdHkueCwgYWxwaGEoMC4xKSkpICsgZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSAxKSArIGd1aWRlcyhjb2xvcj1GQUxTRSkgKyBnZ3RpdGxlKCJTdHJpcCBQbG90IG9mIEluY29tZXMgYnkgQm9yb3VnaCIpICsgbGFicyh4PSJCb3JvdWdoIiwgeT0iSW5jb21lIikNCmBgYA0KDQpTaW1pbGFybHkgdG8gdGhlIGJveHBsb3QsIHRoaXMgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgaW5jb21lIGZvciBlYWNoIG9mIHRoZSBib3JvdWdocy4gV2UgY2FuIHNlZSB3ZSBkbyBub3QgaGF2ZSB0b28gbXVjaCBkYXRhIG9uIFN0YXRlbiBJc2xhbmQgYWdhaW4uDQoNCmBgYHtyfQ0KZ2dwbG90KGNlbnN1cywgYWVzKHg9cmVvcmRlcihjaXR5LngsIC10YWJsZShjaXR5LngpW2NpdHkueF0pKSkgKyBnZW9tX2JhcigpICsgeGxhYigiQ2l0eSIpICsgeWxhYigiTnVtYmVyIG9mIFppcGNvZGVzIikgKyBnZ3RpdGxlKCJOdW1iZXIgb2YgemlwY29kZXMgd2l0aCBvYnNlcnZhdGlvbnMgcGVyIGJvcm91Z2giKQ0KYGBgDQoNCkFnYWluIHRoZSBOQXMgaGVyZSBjb3JyZXNwb25kIHRvIG5vdCBoYXZpbmcgYW4gYWN0dWFsIGluY29tZSB2YWx1ZSBmb3Igc2FpZCB6aXBjb2RlLiBJdCB3YXMgZGVyaXZlZCBmcm9tIHRoZSBhdmVyYWdlIGZvciB0aGF0IG5laWdoYm9yaG9vZC4gVGhpcyBzaG93cyB0aGF0IGFwcHJveGltYXRlbHkgMjUgb2YgdGhlIHppcGNvZGVzIGhhZCBubyBpbmNvbWUgZGF0YSBhbmQgd2Ugd2VyZSBmb3JjZWQgdG8gaW1wdXRlIGl0IHdpdGggdGhlIGF2ZXJhZ2UgZm9yIHRoYXQgbmVpZ2hib3Job29kLiBBbHNvLCB0aGlzIGRhdGEgaXMgY2VydGFpbmx5IGJldHRlciB0aGFuIHRoZSBsYXN0IHNldCwgYWx0aG91Z2ggaWRlYWxseSB3ZSB3b3VsZCBzdGlsbCBoYXZlIG1vcmUgZGF0YSBvbiBRdWVlbnMgYW5kIFN0YXRlbiBJc2xhbmQgZXNwZWNpYWxseSBpZiB0aGVyZSBhcmUgbWFueSBsaXF1b3IgbGljZW5zZXMgYW5kIHNpZGV3YWxrIGNhZmVzIHBvcHBpbmcgdXAgaW4gemlwY29kZXMgd2hpY2ggd2UgYXJlIG1pc3NpbmcuDQoNCiMjIyMgU2lkZXdhbGsgQ2FmZSBMaWNlbnNlIERhdGEgLSBNYXJpa2ENCg0KYGBge3J9DQpxdWVlbnNfbmFtZXM8LXJlYWQuY3N2KCJEYXRhL1FVX0xvY2F0aW9ucy5jc3YiLHN0cmlwLndoaXRlPVRSVUUpDQpicm9va2x5bl9uYW1lczwtcmVhZC5jc3YoIkRhdGEvQktfTG9jYXRpb25zLmNzdiIsc3RyaXAud2hpdGU9VFJVRSkNCm1hbmhhdHRhbl9uYW1lczwtcmVhZC5jc3YoIkRhdGEvTUFfTG9jYXRpb25zLmNzdiIsc3RyaXAud2hpdGU9VFJVRSkNCmJyb254X25hbWVzPC1yZWFkLmNzdigiRGF0YS9CWF9Mb2NhdGlvbnMuY3N2IixzdHJpcC53aGl0ZT1UUlVFKQ0KDQpzaWRld2Fsa3M8LXJlYWQuY3N2KCJEYXRhL1NpZGV3YWxrIENhZmVzL1NpZGV3YWxrX0xpY2Vuc2VzLmNzdiIsc3RyaXAud2hpdGU9VFJVRSwgbmEuc3RyaW5ncz1jKCIiLCJOQSIpKQ0KYGBgDQojIyMjI0RhdGEgUXVhbGl0eQ0KT25lIG9mIHRoZSBtb3N0IGdsYXJpbmcgaXNzdWVzIGluIGRhdGEgcXVhbGl0eSBpcyB0aGUgbmFtaW5nIG9mIHRoZSBjaXRpZXMgaW4gTWFuaGF0dGFuIGFuZCBRdWVlbnMuIENhcGl0YWxpemF0aW9uIGRpZmZlcmVuY2VzIGxpa2UgYmV0d2VlbiAiTkVXIFlPUksiIGFuZCAiTmV3IFlvcmsiIGdyb3VwZWQgY2FmZXMgaW4gdGhlIHNhbWUgY2l0eSB0byBiZSBjYXRlZ29yaXplZCBkaWZmZXJlbnRseS4gU2ltaWxhcmx5LCBjaXRpZXMgaW4gUXVlZW5zIGhhZCBzb21lIGFiYnJldmlhdGlvbiBkaWZmZXJlbmNlcyBsaWtlIGJldHdlZW4gIkxPTkcgSVNMQU5EIENJVFkiIGFuZCAiTE9ORyBJUyBDSVRZIiBvciAiSkFDS1NPTiBIRUlHSFRTIiBhbmQgIkpBQ0tTT04gSFRTIi4gSSBtYW51YWxseSByZXBsYWNlZCBhbGwgYWJicmV2aWF0ZWQgb3Igbm9uLWNhcGl0YWxpemVkIGNpdGllcyB3aXRoIHRoZWlyIGxvbmdlciwgY2FwaXRhbGl6ZWQgZm9ybXMuDQpgYGB7cn0NCnNpZGV3YWxrcyRDSVRZW3NpZGV3YWxrcyRDSVRZPT0iTE9ORyBJUyBDSVRZIl08LSJMT05HIElTTEFORCBDSVRZIg0Kc2lkZXdhbGtzJENJVFlbc2lkZXdhbGtzJENJVFk9PSJNSURETEUgVkxHIl08LSJNSURETEUgVklMTEFHRSINCnNpZGV3YWxrcyRDSVRZW3NpZGV3YWxrcyRDSVRZPT0iSkFDS1NPTiBIVFMiXTwtIkpBQ0tTT04gSEVJR0hUUyINCnNpZGV3YWxrcyRDSVRZW3NpZGV3YWxrcyRDSVRZPT0iTmV3IFlvcmsiXTwtIk5FVyBZT1JLIg0KYGBgDQoNClRoZSBzZWNvbmQgZGF0YSBxdWFsaXR5IGlzc3VlIEkgZW5jb3VudGVyZWQgd2FzIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIG9mIHR3byByZXN0YXVyYW50cy4gUGxvdHRpbmcgYWxsIHJlc3RhdXJhbnRzIHVzaW5nIHFtcGxvdCwgSSBnb3QgdGhlIGZvbGxvd2luZyByZXN1bHQ6DQoNCmBgYHtyfQ0KcW1wbG90KExPTkdJVFVERSxMQVRJVFVERSxkYXRhPXNpZGV3YWxrcyxtYXB0eXBlPSJ0b25lci1saXRlIixjb2xvcj1JKCJwdXJwbGUiKSkNCmBgYA0KTm90ZSB0aGF0IHRoZXJlIGlzIGEgbXlzdGVyaW91cyBkb3QgYWxsIHRoZSB3YXkgd2VzdCBvZiBIYXJyaXNidXJnLCBQQS4gVGhpcyBzaG91bGQgbm90IGJlIHRoZSBjYXNlIHNpbmNlIHRoZSBkYXRhIHNob3VsZCBvbmx5IGFwcGx5IHRvIHRoZSBDaXR5IGFuZCBib3JvdWdocyBvZiBOZXcgWW9yaywgc28gSSBwbG90dGVkIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIHRvIHNlZSBhbnkgb3V0bGllcnMuDQpgYGB7cn0NCmxhdDwtZ2dwbG90KHNpZGV3YWxrcywgYWVzKHg9TEFUSVRVREUpKStnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0uMDEpK2dndGl0bGUoIkxhdGl0dWRlIEhpc3RvZ3JhbSIpDQpsb25nPC1nZ3Bsb3Qoc2lkZXdhbGtzLGFlcyh4PUxPTkdJVFVERSkpK2dlb21faGlzdG9ncmFtKGJpbndpZHRoPS4wMSkrZ2d0aXRsZSgiTG9uZ2l0dWRlIEhpc3RvZ3JhbSIpDQpncmlkLmFycmFuZ2UobGF0LGxvbmcsbnJvdz0yKQ0KYGBgDQpZb3UgY2FuIGJhcmVseSBzZWUgc29tZSBwb2ludHMgYmVvdyB0aGUgNDAuMjUgbGF0aXR1ZGUgYW5kIC03NyBsb25naXR1ZGUsIHdoaWNoIHNlZW0gdG8gYmUgcXVpdGUgb2ZmIGZyb20gdGhlIHJlc3QuIE5leHQsIEkgem9vbWVkIGluIG9uIHRob3NlIHZhbHVlczoNCmBgYHtyfQ0KbGF0PC1nZ3Bsb3Qoc2lkZXdhbGtzLCBhZXMoeD1MQVRJVFVERSkpK2dlb21faGlzdG9ncmFtKGJpbndpZHRoPS4yNSkrZ2d0aXRsZSgiWm9vbWVkIExhdGl0dWRlIEhpc3RvZ3JhbSIpK3hsaW0oMzksNDAuMjUpDQpsb25nPC1nZ3Bsb3Qoc2lkZXdhbGtzLGFlcyh4PUxPTkdJVFVERSkpK2dlb21faGlzdG9ncmFtKGJpbndpZHRoPS4yNSkrZ2d0aXRsZSgiWm9vbWVkIExvbmdpdHVkZSBIaXN0b2dyYW0iKSt4bGltKC04MCwtNzcpDQpncmlkLmFycmFuZ2UobGF0LGxvbmcsbnJvdz0yKQ0KYGBgDQpUaGVyZSBhcmUgdHdvIGRhdGFwb2ludHMgdGhhdCBoYXZlIGFuIGFibm9ybWFsbHkgbG93IExhdGl0dWRlIGFuZCBMb25naXR1ZGUgYXMgY29tcGFyZWQgdG8gdGhlIHJlc3Qgb2YgdGhlIGRhdGEuIFRha2luZyBhIGxvb2sgYXQgdGhlc2UgZGF0YSBwb2ludHMsIEkgaWRlbnRpZmllZCB0aGVtIGFzIHRoZSBmb2xsb3dpbmcgYnVzaW5lc3NlczoNCmBgYHtyfQ0Kc2lkZXdhbGtzICU+JSBmaWx0ZXIoTE9OR0lUVURFIDwgLTc3KSAlPiUgc2VsZWN0KEJVU0lORVNTX05BTUUyLExPTkdJVFVERSxMQVRJVFVERSkNCmBgYA0KTG9va2luZyB0aGVzZSBsb2NhdGlvbnMgdXAgb24gR29vZ2xlLCBpdCBsb29rcyBsaWtlIHRoZXJlIHdhcyBhbiBlcnJvciBpbiBlbnRlcmluZyB0aGVpciBsb25naXR1ZGUgYW5kIGxhdGl0dWRlLiBUaGUgY29ycmVjdCB2YWx1ZXMgYXJlICg0MC43MjAxNywtNzMuOTk2OCkgZm9yIE11bGJlcnJ5IFN0cmVldCBCYXIgYW5kICg0MC43OTU5MywtNzMuOTM1NykgZm9yIFByaW1lIE9uZSAxNi4gSSBoYXZlIGNvcnJlY3RlZCB0aGVzZSB2YWx1ZXMgaW4gYW5vdGhlciBDU1YsIHdoaWNoIEkgd2lsbCB1c2UgZnJvbSBoZXJlIG9uLg0KDQpgYGB7cn0NCnNpZGV3YWxrczwtcmVhZC5jc3YoIkRhdGEvU2lkZXdhbGsgQ2FmZXMvU2lkZXdhbGtfTGljZW5zZXMyLmNzdiIsc3RyaXAud2hpdGU9VFJVRSwgbmEuc3RyaW5ncz1jKCIiLCJOQSIpKQ0Kc2lkZXdhbGtzJENJVFlbc2lkZXdhbGtzJENJVFk9PSJMT05HIElTIENJVFkiXTwtIkxPTkcgSVNMQU5EIENJVFkiDQpzaWRld2Fsa3MkQ0lUWVtzaWRld2Fsa3MkQ0lUWT09Ik1JRERMRSBWTEciXTwtIk1JRERMRSBWSUxMQUdFIg0Kc2lkZXdhbGtzJENJVFlbc2lkZXdhbGtzJENJVFk9PSJKQUNLU09OIEhUUyJdPC0iSkFDS1NPTiBIRUlHSFRTIg0Kc2lkZXdhbGtzJENJVFlbc2lkZXdhbGtzJENJVFk9PSJOZXcgWW9yayJdPC0iTkVXIFlPUksiDQoNCnFtcGxvdChMT05HSVRVREUsTEFUSVRVREUsZGF0YT1zaWRld2Fsa3MsbWFwdHlwZT0idG9uZXItbGl0ZSIsY29sb3I9SSgicHVycGxlIikpDQoNCmBgYA0KTm93IHRoYXQgdGhlIG9mZmVuZGluZyBkYXRhcG9pbnRzIGhhdmUgYmVlbiBmaXhlZCwgdGhlIHFtcGxvdCBhY2N1cmF0ZWx5IHpvb21zIGluIG9uIHRoZSBOZXcgWW9yayBhcmVhLiBIb3dldmVyLCBzaW5jZSB0aG9zZSB0d28gZGF0YSBwb2ludHMgZXhpc3QsIGl0IGlzIGVudGlyZWx5IHBvc3NpYmxlIHRoYXQgdGhlcmUgYXJlIG90aGVyIG1pcy1tYXBwZWQgbGFuZ2l0dWRlIGFuZCBsb25naXR1ZGUgcG9pbnRzLiBIb3dldmVyLCB3aXRob3V0IG1hbnVhbGx5IGdvaW5nIHRocm91Z2ggZWFjaCBkYXRhIHBvaW50LCBpdCBpcyBub3QgZWFzeSB0byBkZWR1Y2Ugd2hlcmUuDQoNCiMjIyMjIE1pc3NpbmcgRGF0YQ0KVG8gZnVydGhlciBpbnZlc3RpZ2F0ZSBkYXRhIHF1YWxpdHksIEkgdG9vayBhIGxvb2sgYXQgdGhlIG1haW4gY29sdW1ucyB0aGF0IGlkZW50aWZpZWQgYSBidXNpbmVzcyAtIGl0cyBsaWNlbnNlIG51bWJlciwgbGljZW5zZSBzdGF0dXMsIGJ1c2luZXNzIG5hbWUgKGJyb2tlbiBpbnRvIHR3byksIGJ1aWxkaW5nIG51bWJlciwgc3RyZWV0LCBjaXR5LCBzdGF0ZSBhbmQgemlwLiBUbyBkaXNwbGF5IHRoaXMgZGF0YSwgSSB1c2VkIGEgdmlzbmEgcGxvdCB3aGljaCBoaWdobGlnaHRzIGFueSBjb2x1bW5zIHdpdGggbWlzc2luZyB2YWx1ZXMgYW5kIHNob3dzIHRoZSBmcmVxdWVuY3kgb2YgdGhlIGNvbWJpbmF0aW9ucyBvZiB0aG9zZSBjb2x1bW5zLg0KYGBge3IgZmlnLmhlaWdodD02fQ0KdmlzbmEoc2lkZXdhbGtzWywxOjldKQ0KYGBgDQpUaGUgdmlzbmEgc2hvd3MgdGhhdCBvbmx5IHR3byB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcyAtIGxpY2Vuc2UgbnVtYmVyIGFuZCBidXNpbmVzcyBuYW1lIG51bWJlciAyLiBUaGVzZSBhcmUgZXhwZWN0ZWQgLSBvbmx5IGxpY2Vuc2VzIHRoYXQgaGF2ZSBiZWVuIGFwcHJvdmVkIHdvdWxkIGhhdmUgYSBsaWNlbnNlIG51bWJlciwgYW5kIHNvbWUgYnVzaW5lc3NlcyBkbyBub3QgbmVlZCB0byB1c2UgYSBzZWNvbmQgbGluZSBmb3IgdGhlaXIgYnVzaW5lc3MgbmFtZS4gICANCg0KTmV4dCwgSSBsb29rZWQgYXQgYWRkaXRpb25hbCBhcHBsaWNhdGlvbiBpbmZvcm1hdGlvbiB3aGljaCBpbmNsdWRlcyB0aGUgc2lkZXdhbGsgY2FmZSB0eXBlLCB0aGUgc3F1YXJlIGZvb3RhZ2UgcmVxdWVzdGVkLCBudW1iZXIgb2YgdGFibGVzLCBudW1iZXIgb2YgY2hhaXJzLCBEZXBhcnRtZW50IG9mIEhlYWx0aCBhbmQgTWVudGFsIEh5Z2llbmUgKERPSE1IKSBpZGVudGlmaWNhdGlvbiBudW1iZXIsIHRoZSByZXN0YXVyYW50J3MgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSwgdGhlIGNvbW11bml0eSBkaXN0cmljdCBhbmQgY2l0eSBjb3VuY2lsIGRpc3RyaWN0IGl0IGJlbG9uZ3MgdG8sIGFuZCB0aGUgVVJMIGZvciB0aGUgd2Vic2l0ZSBvZiB0aGUgTllDIENvbW11bml0eSBEaXN0cmljdC4gDQoNCmBgYHtyIGZpZy5oZWlnaHQ9Nn0NCnZpc25hKHNpZGV3YWxrc1ssMTA6MTldKQ0KYGBgDQpUd28gdmFyaWFibGVzIGhhdmUgbWlzc2luZyB2YWx1ZXMgLSB0aGUgc3F1YXJlIGZvb3RhZ2Ugb2YgdGhlIHNpZGV3YWxrIGNhZmUgYW5kIHRoZSBEZXBhcnRtZW50IG9mIEhlYWx0aCBhbmQgTWVudGFsIEh5Z2VuZSBpZGVudGlmaWNhdGlvbiBudW1iZXIuIFRoZXNlIGFyZSBpdGVtcyB0aGF0IHRoZSByZXN0YXVyYW50IHdvdWxkIGhhdmUgdG8gcHJvdmlkZSBkdXJpbmcgdGhlaXIgYXBwbGljYXRpb24gcHJvY2VzcywgYW5kIG1heSBub3QgaGF2ZSBiZWVuIGF2YWlsYWJsZSBhdCBzdWJtaXNzaW9uLiBUaGVzZSBtaXNzaW5nIHZhbHVlcyBzaG91bGQgbm90IGltcGVkZSB3aXRoIG91ciBhbmFseXNpcy4gSG93ZXZlciwgaWYgd2Ugd2FudGVkIHRvIGNyb3NzLXJlZmVyZW5jZSB0aGUgaGVhbHRoIGdyYWRlcyBmcm9tIHRoZSBET0hNSCBkYXRhc2V0cywgd2Ugd291bGQgaGF2ZSBpc3N1ZXMgd2l0aCB0aGUgbWlzc2luZyBJRCBudW1iZXJzLiAgDQoNClRoZSBuZXh0IHNldCBvZiBjb2x1bW5zIHRvIGFuYWx5emUgaXMgdGhlIHNldCBvZiBhcHBsaWNhdGlvbi1zcGVjaWZpYyBmaWVsZHMuIFRoZXNlIGluY2x1ZGUgdGhlIGFwcGxpY2F0aW9uIElELCBhbmQgdGhlIHNpZGV3YWxrIGNhZmUgdHlwZSwgc3F1YXJlIGZvb3RhZ2UsIG51bWJlciBvZiB0YWJsZXMgYW5kIGNoYWlycyBwcm92aWRlZCBieSB0aGUgYXBwbGljYW50LiBJdCBoYXMgdGhlIGFwcCBzdGF0dXMsIHRoZSBkYXRlIGF0IHdoaWNoIHRoZSBhcHAgc3RhdHVzIHdhcyByZWFjaGVkLCBleHBpcmF0aW9uIGRhdGUsIHRoZSBleHBpcmF0aW9uIGRhdGUgb2YgdGhlIHRlbXBvcmFyeSBvcGVyYXRpbmcgb3JkZXIoIlRPTyIgLSBpZiBhdmFpbGFibGUpLCB0aGUgYXBwbGljYXRpb24gc3VibWl0IGRhdGUsIGFuZCB3aGV0aGVyIHRoZSBhcHBsaWNhdGlvbiBoYXMgYmVlbiByZWNlaXZlZC4NCg0KYGBge3IgZmlnLmhlaWdodD03fQ0KdmlzbmEoc2lkZXdhbGtzWywyMDoyOV0pDQpgYGANClRoZXJlIHNlZW0gdG8gYmUgYSBmZXcgbWlzc2luZyBmaWVsZHMgaW4gdGhlIHByb3ZpZGVkIHNpZGV3YWxrIGNhZmUgc3F1YXJlZm9vdGFnZSBkYXRhLiBIb3dldmVyLCB3ZSB3aWxsIG5vdCBiZSBmb2N1c2luZyBvbiB0aGlzIGRhdGEgcG9pbnQgaW4gb3VyIGFuYWx5c2lzLiBFeHBpcmF0aW9uIGRhdGVzIGFyZSBhbHNvIG1pc3NpbmcgZnJvbSBjZXJ0YWluIGxpY2Vuc2VzLiBGaWx0ZXJpbmcgb3V0IHRob3NlIGxpY2Vuc2VzIGFuZCBsb29raW5nIGF0IHRoZWkgQVBQX1NUQVRVUywgd2UgY2FuIHNlZSB0aGF0IHRob3NlIGxpY2Vuc2VzIHRoYXQgYXJlIGluIHJldmlldyBtYXkgbm90IGhhdmUgYW4gYXNzaWduZWQgZXhwaXJhdGlvbiBzdGF0dXMuIEluZGVlZCwgdGhpcyBtYWtlcyBzZW5zZSB3aXRoIG5ldyBhcHBsaWNhdGlvbnMgdGhhdCBoYXZlIG5vdCBiZWVuIGdpdmVuIGFuIGV4cGlyYXRpb24gZGF0ZS4NCg0KYGBge3J9DQptaXNzaW5nX2V4cGlyYXRpb248LXNpZGV3YWxrcyAlPiUgZmlsdGVyKGlzLm5hKEVYUElSQVRJT05fREFURSkpICU+JSBzZWxlY3QoQVBQX1NUQVRVUykNCm1pc3NpbmdfZXhwaXJhdGlvbiAlPiUgdW5pcXVlKCkNCmBgYA0KQSBzaW1pbGFyIGV4cGxhbmF0aW9uIGFyaXNlcyBhYm91dCB0aGUgYXBwbGljYXRpb25zIHdpdGggYSBtaXNzaW5nIFRlbXBvcmFyeSBPcGVyYXRpbmcgT3JkZXIgKCJUT08iKSBkYXRlIC0gb25seSB0aG9zZSBhcHBsaWNhdGlvbnMgdGhhdCBoYXZlIGJlZW4gZ3JhbnRlZCBhIFRPTyBpbiBjYXNlcyB3aGVyZSB0aGUgb2xkIGxpY2Vuc2UgaGFzIGV4cGlyZWQgYnV0IHRoZSByZW5ld2FsIGlzIGluIHRoZSBwcm9jZXNzIG9mIGJlaW5nIHJldmlld2VkLiAgDQoNClRoZSByZW1haW5pbmcgY29sdW1zIHRyYWNrIHRoZSBzdGF0dXMgb2YgdGhlIGxpY2Vuc2UgYXBwcm92YWwgcHJvY2VzcyB0aHJvdWdoIHZhcmlvdXMgc3RhZ2VzIGFuZCB3aWxsIG5vdCBiZSB1c2VkIGluIHRoaXMgYW5hbHlzaXMuIFRoZXJlZm9yZSwgSSB3aWxsIG5vdCBzcGVuZCB0aW1lIG9uIGludmVzdGlnYXRpbmcgYW55IG1pc3NpbmcgZGF0YS4gDQoNCiMjIyMjIENvbWJpbmluZyB3aXRoIE5laWdoYm9yaG9vZCBkYXRhDQpGcm9tIEFkYW0ncyBhbmFseXNpcywgd2UgaGF2ZSBhIG1hc3Rlcl96aXAgbGlzdCBvZiBhbGwgb2YgdGhlIHppcCBjb2RlcyBtYXBwZWQgdG8gdGhlIG5laWdoYm9yaG9vZHMgaW4gZWFjaCBib3JvdWdoLiBJIHdpbGwgY3JlYXRlIGEgc2VwYXJhdGUgZGF0YWZyYW1lIGNhbGxlZCBzaWRld2Fsa3NfbmJoIHdoaWNoIGlzIGpvaW5lZCB3aXRoIHRoZSBtYXN0ZXJfemlwIG9uIFppcCBDb2RlLiBJIHdpbGwgdXNlIHRoZSBuZWlnaGJvcmhvb2QgY29sdW1uLCAnbmJoJywgdG8gZ3JvdXAgdGhlIHNpZGV3YWxrIGNhZmVzIGJ5IGJvcm91Z2ggYW5kIG5laWdoYm9yaG9vZC4gSSB0aGVuIGZpbHRlcmVkIHRoZSByZXN1bHRpbmcgZGF0YWZyYW1lIHRvIGZpbmQgYW55IG1pc3NpbmcgdmFsdWVzIGZyb20gdGhlIGpvaW4uDQoNCmBgYHtyfQ0KDQptYXN0ZXJfemlwPC1yZWFkLmNzdigiRGF0YS96aXBzX21hc3Rlcl9ub19taXNzaW5nX25icmguY3N2Iiwgc3RyaXAud2hpdGU9VFJVRSkNCg0Kc2lkZXdhbGtzX25iaCA8LSBtZXJnZShzaWRld2Fsa3MsIG1hc3Rlcl96aXAsIGJ5PSJaSVAiLCBhbGwueD1UUlVFKQ0KDQpzaWRld2Fsa3NfbmJoICU+JSBmaWx0ZXIoaXMubmEobmJoKSkgJT4lIHNlbGVjdChaSVApDQpgYGANClRoZXJlIHdlcmUgYSBsb3Qgb2YgbWlzc2luZyB2YWx1ZXMgb3JpZ2luYWxseSBmcm9tIHRoZSBtYXN0ZXJfemlwIGFuYWx5c2lzLCB3aGljaCBJIGhhdmUgbWFudWFsbHkgcmVzZWFyY2hlZCBhbmQgZmlsbGVkIGluIGluIHRoZSBfbmJyaC5jc3YuIE5vdyB0aGVyZSBhcmUgbm8gQ2FmZXMgd2l0aG91dCBhIG5laWdoYm9yaG9vZCBkZXNpZ25hdGlvbi4gDQoNCg0KDQojIyMjIExpcXVvciBMaWNlbnNlIERhdGEgLSBSb2hhbg0KDQojIyA0LiBFeGVjdXRpdmUgU3VtbWFyeSAtIEFsbA0KDQpJbnRlcmFjdGl2ZSBNYXAgb2YgTmV3IFlvcmsgQ2l0eQ0KDQpQcm92aWRlIGEgc2hvcnQgbm9udGVjaG5pY2FsIHN1bW1hcnkgb2YgdGhlIG1vc3QgcmV2ZWFsaW5nIGZpbmRpbmdzIG9mIHlvdXIgYW5hbHlzaXMgd2l0aCBubyBtb3JlIHRoYW4gMyBzdGF0aWMgZ3JhcGhzIG9yIG9uZSBpbnRlcmFjdGl2ZSBncmFwaCAob3IgbGluayksIHdyaXR0ZW4gZm9yIGEgbm9udGVjaG5pY2FsIGF1ZGllbmNlLiBUaGUgbGVuZ3RoIHNob3VsZCBiZSBhcHByb3hpbWF0ZWx5IDIgcGFnZXMgKGlmIHdlIHdlcmUgdXNpbmcgcGFnZXMuLi4pIERvIG5vdCBzaG93IGNvZGUsIGFuZCB0YWtlIGV4dHJhIGNhcmUgdG8gY2xlYW4gdXAgeW91ciBncmFwaHMsIGVuc3VyaW5nIHRoYXQgYmVzdCBwcmFjdGljZXMgZm9yIHByZXNlbnRhdGlvbiBhcmUgZm9sbG93ZWQuDQouICBOb3RlOiB0aGUgdGlwcyBiZWxvdyBhcmUgbm90IGludGVuZGVkIHRvIGJlIGEgY29tcGxldGUgbGlzdCBvZiBldmVyeXRoaW5nIHdlJ3ZlIGNvdmVyZWQgdGhpcyBzZW1lc3RlciBvbiBkZXNpZ25pbmcgYSBzdWNjZXNzZnVsIGdyYXBoLiAgSXQncyBtZWFudCB0byBoZWxwIHlvdSBhdm9pZCBzb21lIGNvbW1vbg0KcHJvYmxlbXMuDQouICBUaXRsZSwgYXhpcyBsYWJlbHMsIHRpY2sgbWFyayBsYWJlbHMsIGFuZCBsZWdlbmRzIHNob3VsZCBiZSBjb21wcmVoZW5zaWJsZSAoZWFzeSB0byB1bmRlcnN0YW5kKSBhbmQgbGVnaWJsZSAoZWFzeSB0byByZWFkIC8gZGVjaXBoZXIpLg0KLiAgVGljayBtYXJrcyBzaG91bGQgbm90IGJlIGxhYmVsZWQgaW4gc2NpZW50aWZpYyBub3RhdGlvbiBvciB3aXRoIGxvbmcgc3RyaW5ncyBvZiB6ZXJvcywgc3VjaCBhcyAzMDAwMDAwMDAwLiBJbnN0ZWFkLCBjb252ZXJ0IHRvIHNtYWxsZXIgbnVtYmVycyBhbmQgY2hhbmdlIHRoZSB1bml0czogMzAwMDAwMDAwMCBiZWNvbWVzICIzIiBhbmQgdGhlIGF4aXMgbGFiZWwgImJpbGxpb25zIG9mIHZpZXdzIi4NCi4gIFVuaXRzIHNob3VsZCBiZSBpbnR1aXRpdmUgKEV4dHJlbWUgZXhhbXBsZTogYW4gYXhpcyBsYWJlbGVkIGluIG1vbnRoL2RheS95ZWFyIGZvcm1hdCBpcyBpbnR1aXRpdmUsIG9uZSBsYWJlbGVkIGluIHNlY29uZHMgc2luY2UgSmFudWFyeSAxLCAxOTcwIGlzIG5vdC4pDQouICBUaGUgZm9udCBzaXplIHNob3VsZCBiZSBsYXJnZSBlbm91Z2ggdG8gcmVhZCBjbGVhcmx5LiBUaGUgZGVmYXVsdCBpbiBnZ3Bsb3QyIGlzIGdlbmVyYWxseSB0b28gc21hbGwuIFlvdSBjYW4gZWFzaWx5IGNoYW5nZSBpdCBieSBwYXNzaW5nIHRoZSBiYXNlIGZvbnQgc2l6ZSB0byB0aGUgdGhlbWUsIHN1Y2ggYXMgICsgdGhlbWVfZ3JleSgxNikuIChUaGUgZGVmYXVsdCBiYXNlIGZvbnQgc2l6ZSBpcyAxMS4pDQouICBUaGUgb3JkZXIgb2YgaXRlbXMgb24gdGhlIGF4ZXMgYW5kIGxlZ2VuZHMgaXMgbG9naWNhbC4gKEFscGhhYmV0aWNhbCBpcyBvZnRlbiBub3QgbG9naWNhbC4pIC4gIENvbG9ycyBzaG91bGQgYmUgY29sb3IgdmlzaW9uIGRlZmljaWVuY3kgZnJpZW5kbHkuIC4gIElmIGEgbGVnZW5kIGlzIHRha2luZyB1cCB0b28gbXVjaCBzcGFjZSBvbiB0aGUgcmlnaHQsIG1vdmUgaXQgdG8gdGhlIGJvdHRvbS4gLiAgSWYgY2F0ZWdvcmljYWwgdmFyaWFibGUgbGV2ZWxzIGFyZSBsb25nLCBzZXQgdXAgdGhlIGdyYXBoIHNvIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBvbiB0aGUgeS1heGlzDQphbmQgdGhlIG5hbWVzIGFyZSBob3Jpem9udGFsLiAgQSBiZXR0ZXIgb3B0aW9uLCBpZiBwb3NzaWJsZSwgaXMgdG8gc2hvcnRlbiB0aGUgbmFtZXMgb2YgdGhlIGxldmVscy4NCi4gIE5vdCBhbGwgRURBIGdyYXBocyBsZW5kIHRoZW1zZWx2ZXMgdG8gcHJlc2VudGF0aW9uLCBlaXRoZXIgYmVjYXVzZSB0aGUgZ3JhcGggZm9ybSBpcyBoYXJkIHRvIHVuZGVyc3RhbmQgd2l0aG91dCBwcmFjdGljZSBvciBpdCdzIG5vdCB3ZWxsIGxhYmVsZWQuIFRoZSBsYWJlbGluZyBwcm9ibGVtIGNhbiBiZSBzb2x2ZWQgYnkgYWRkaW5nIHRleHQgaW4gYW4gaW1hZ2UgZWRpdG9yLiBUaGUgZG93bnNpZGUgaXMgdGhhdCBpdCBpcyBub3QgcmVwcm9kdWNpYmxlLiBJZiB5b3Ugd2FudCB0byBnbyB0aGlzIHJvdXRlLCBQYWludGJydXNoIGlzIGEgZnJlZSBhbmQgc2ltcGxlIGJpdG1hcCBpbWFnZSBlZGl0b3IgZm9yIHRoZSBNYWM6IGh0dHBzOi8vcGFpbnRicnVzaC5zb3VyY2Vmb3JnZS5pby8gVGhlcmUgYXJlIG1hbnkgb3RoZXIgb3B0aW9ucy4gDQoNCiMjIDUuIE1haW4gQW5hbHlzaXMgLSBBbGwNCg0KUHJvdmlkZSBhIGRldGFpbGVkLCB3ZWxsLW9yZ2FuaXplZCBkZXNjcmlwdGlvbiBvZiB5b3VyIGZpbmRpbmdzLCBpbmNsdWRpbmcgdGV4dHVhbCBkZXNjcmlwdGlvbiwgZ3JhcGhzLCBhbmQgY29kZS4gIFlvdXIgZm9jdXMgc2hvdWxkIGJlIG9uIGJvdGggdGhlIHJlc3VsdHMgYW5kIHRoZSBwcm9jZXNzLiBJbmNsdWRlLCBhcyByZWFzb25hYmxlIGFuZCByZWxldmFudCwgYXBwcm9hY2hlcyB0aGF0IGRpZG4ndCB3b3JrLCBjaGFsbGVuZ2VzLCB0aGUgZGF0YSBjbGVhbmluZyBwcm9jZXNzLCBldGMuIC4gIFRoZSBndWlkZWxpbmVzIGZvciB0aGUgRXhlY3V0aXZlIFN1bW1hcnkgYWJvdmUgZG8gTk9UIGFwcGx5IHRvIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuDQpZb3VyIG1haW4gY29uY2VybiBpcyBkZXNpZ25pbmcgZ3JhcGhzIHRoYXQgcmV2ZWFsIHBhdHRlcm5zIGFuZCB0cmVuZHMuIC4gIEFzIG5vdGVkIGluIEhtayAjNCwgZG8gbm90IHVzZSBjaXJjbGVzLCB0aGF0IGlzOiBidWJibGVzLCBwaWUgY2hhcnRzLCBvciBwb2xhciBjb29yZGluYXRlcy4gLiAgVXNlIHN0YWNrZWQgYmFyIGNoYXJ0cyBzcGFyaW5nbHkuIFRyeSBncm91cGVkIGJhciBjaGFydHMgYW5kIGZhY2V0aW5nIGFzIGFsdGVybmF0aXZlcywgYW5kIG9ubHkNCmNob29zZSBzdGFja2VkIGJhciBjaGFydHMgaWYgdGhleSB0cnVseSBkbyBhIGJldHRlciBqb2IgdGhhbiB0aGUgYWx0ZXJuYXRpdmVzIGZvciBvYnNlcnZpbmcgcGF0dGVybnMuIA0KDQojIyMjIFppcCBDb2RlLCBOZWlnaGJvcmhvb2QgYW5kIExhbmQgVmFsdWUgRGF0YSAtIEFkYW0NCg0KT25lIG9mIG91ciBvdGhlciBtYWluIHF1ZXN0aW9ucyBpbiB0aGlzIHByb2plY3Qgd2FzIGxvb2tpbmcgYXQgaG93IHRoZSB3ZWFsdGggd2FzIGRpc3RyaWJ1dGVkIGFtb25nc3QgZWFjaCBvZiB0aGUgemlwY29kZXMgaW4gdGhlIGZpdmUgYm9yb3VnaHMuIFVzaW5nIHRoZSBjZW5zdXMgZGF0YSBmcm9tIHRoZSBzdXBlcnppcCBkYXRhc2V0IGFuZCBtYXBwaW5nIHRoYXQgdG8gdGhlIHppcGNvZGUgc2hhcGVmaWxlIGRvY3VtZW50IHdlIGZvdW5kLCB3ZSBtYW5hZ2VkIHRvIHZpc3VhbGl6ZSB0aGUgd2VhbHRoIG9mIGVhY2ggbmVpZ2hib3Job29kLiBXZSB3aWxsIGxhdGVyIGJlIGFibGUgdG8gcGxvdCB0aGUgbGljZW5zZSBhcHBsaWNhdGlvbnMgb3ZlciB0aGlzIG1hcHBpbmcgb2YgdGhlIHdlYWx0aCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KbWFwIDwtIGdldF9tYXAoIk5ldyBZb3JrIENpdHkiLCBzb3VyY2UgPSAiZ29vZ2xlIiwgbWFwdHlwZSA9ICJ0b25lciIsIHpvb20gPSAxMSkNCg0KZ2dtYXAobWFwLCBiYXNlX2xheWVyID0gZ2dwbG90KGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGNvbG9yID0gYXZnKSwgZGF0YSA9IGNlbnN1cykpICArIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhPTAuNykgKyBzY2FsZV9jb2xvcl92aXJpZGlzKCkgKyBnZ3RpdGxlKCJBdmVyYWdlIFNhbGFyeSBvZiBlYWNoIFppcGNvZGUgaW4gTmV3IFlvcmsiKQ0KYGBgDQoNClRoaXMgbWFwIHNob3dzIGFsbCBvZiB0aGUgemlwY29kZXMgYW5kIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGF0IGJ5IGZhciB0aGUgZ3JlYXRlc3QgaW5jb21lcyBwZXIgemlwY29kZSBhcmUgaW4gdGhlIFVwcGVyIEVhc3QgU2lkZSBhbmQgVXBwZXIgV2VzdCBTaWRlLiBUaGVyZSBhcHBlYXJzIHRvIGJlIHNvbWUgZ3JleSBhcmVhYSBpZiB5b3UgbG9vayBjbG9zZWx5IGF0IHRoZSBVcHBlciBXZXN0IFNpZGUuIFRoaXMgY29ycmVzcG9uZHMgdG8gYWJvdXQgMTUwIG9uIHRoZSBjb2xvciBtYXAuIFRoZW4sIG1pZHRvd24gYW5kIGRvd250b3duIGFsc28gaGF2ZSBoaWdoIGluY29tZXMuIEFmdGVyIHRoYXQsIHRoZXkgYWxsIGZhbGwgdG93YXJkcyB0aGUgYm90dG9tIG9mIHRoZSBzcGVjdHJ1bS4gVGh1cywgSSB0aGluayBpdCB3b3VsZCBiZSB1c2VmdWwgdG8gbWFrZSBhIG1hcCBzaG93aW5nIG9ubHkgTWFuaGF0dGFuLCBhbmQgdGhlbiB0aGUgZXZlcnl0aGluZyBleGNsdWRpbmcgTWFuaGF0dGFuLiBUaGlzIHNob3VsZCBnaXZlIHVzIGEgYmV0dGVyIGlkZWEgb2Ygd2hhdCBhcmVhcyBvdXRzaWRlIG9mIE1hbmhhdHRhbiBtaWdodCBzZWUgbW9yZSBiYXIvY2FmZSBvcGVuaW5ncy4NCg0KDQpgYGB7cn0NCmNlbnN1c19tYW5oYXR0YW4gPC0gY2Vuc3VzICU+JSBkcGx5cjo6ZmlsdGVyKGNpdHkueCA9PSAnTWFuaGF0dGFuJykNCmdnbWFwKG1hcCwgYmFzZV9sYXllciA9IGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvciA9IGF2ZyksIGRhdGEgPSBjZW5zdXNfbWFuaGF0dGFuKSkgICsgZ2VvbV9wb2ludChzaXplID0gNCwgYWxwaGE9MC43KSArIHNjYWxlX2NvbG9yX3ZpcmlkaXMoKSArIGdndGl0bGUoIkF2ZXJhZ2UgU2FsYXJ5IG9mIGVhY2ggWmlwY29kZSBpbiBNYW5oYXR0YW4iKQ0KYGBgDQoNClRoaXMgZ3JhcGggc2hvd3MgYWxsIG9mIHRoZSBkYXRhIGZpbHRlcmVkIGZvciBNYW5oYXR0YW4gb25seS4gV2UgY2FuIHNlZSB0aGF0IG9uY2Ugd2UgZ2V0IHRvIEhhcmxlbSBhbmQgYWJvdmUsIHRoZSBhdmVyYWdlIG1lZGlhbiBpbmNvbWUgcGVyIG5laWdoYm9yaG9vZCBkcm9wcyBoZWF2aWx5LiBUaGUgcmVzdCBvZiB0aGUgY2l0eSBpcyBwcmV0dHkgc2ltaWxhci4NCg0KDQpgYGB7cn0NCmJyb29rbHluX21hcDIgPC0gZ2V0X21hcCgiQnJvb2tseW4sIE5ZIiwgIHNvdXJjZSA9ICJnb29nbGUiLCB6b29tID0gMTIsIG1hcHR5cGU9InRvbmVyIiwgY29sb3I9ImJ3IikNCmNlbnN1c19icm9va2x5biA8LSBjZW5zdXMgJT4lIGRwbHlyOjogZmlsdGVyKGNpdHkueCA9PSAnQnJvb2tseW4nKQ0KZ2dtYXAoYnJvb2tseW5fbWFwMiwgYmFzZV9sYXllciA9IGdncGxvdChhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvciA9IGF2ZyksIGRhdGEgPSBjZW5zdXNfYnJvb2tseW4pKSAgKyBnZW9tX3BvaW50KHNpemUgPSA0LCBhbHBoYT0wLjcpICsgc2NhbGVfY29sb3JfdmlyaWRpcygpICsgZ2d0aXRsZSgiQXZlcmFnZSBTYWxhcnkgb2YgZWFjaCBaaXBjb2RlIGluIEJyb29rbHluIikNCmBgYA0KDQpUaGlzIHNob3dzIHVzIHRoZSBhdmVyYWdlIHNhbGFyeSB0aHJvdWdob3V0IHRoZSB6aXBjb2RlcyBpbiBCcm9va2x5biBhbmQgd2UgY2FuIHNlZSB0aGF0IGl0IGlzIGhpZ2hlc3QgaW4gZ2VuZXJhbCB3aGVuIHRoZSBuZWlnaGJvcmhvb2QgaXMgY2xvc2VyIHRvIE1hbmhhdHRhbi4NCg0KQXQgdGhpcyBwb2ludCwgSSBoYWQgcmVhbGl6ZWQgdGhhdCBpdCB3b3VsZCBiZSBoYXJkIHRvIG1ha2UgYSBjb21iaW5lZCBtYXAgaWxsdXN0cmF0aW5nIGJvdGggdGhlIGluY29tZSBkYXRhIGFuZCBsaWNlbnNlcyBkYXRhIGJ5IGRpc3BsYXlpbmcgdGhlIHppcHMgbGlrZSB0aGlzLiBUaHVzLCBJIGZvdW5kIHppcGNvZGUgc2hhcGVmaWxlIGRhdGEgZm9yIE5ZQyBhbmQgaW1wb3J0ZWQgaXQgaW4gb3JkZXIgdG8gbWFrZSBiZXR0ZXIgbWFwcyB0byBiZSB1c2VkIGFzIGEgYmFzZSBsYXllciBmb3IgdGhlIGxpY2Vuc2luZyBkYXRhLiBJIGFsc28gYmVndW4gbGFiZWxsaW5nIHRoZSBuZWlnaGJvcmhvb2RzIHVzaW5nIHRoZSBsYWJlbHMgTWFyaWthIGhhZCBjcmVhdGVkLg0KDQpgYGB7cn0NCnBsb3R0aW5nX3ppcHMxPC1yZWFkLmNzdigiRGF0YS9OWV9zaGFwZWZpbGVzLmNzdiIpDQpgYGANCg0KDQpgYGB7cn0NCmdnbWFwKG1hcCkgKyBnZW9tX3BvbHlnb24oYWVzKGZpbGwgPSBhdmcsIHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZGF0YSA9IHBsb3R0aW5nX3ppcHMxLCBhbHBoYSA9IDAuOCkgKyBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgQXZlcmFnZSBXZWFsdGggcGVyIE5laWdoYm9yaG9vZCBpbiBOZXcgWW9yayIpIA0KYGBgDQoNCkhlcmUgd2Ugc2VlIHRoYXQgTWFuaGF0dGFuIGhhcyB0aGUgaGlnaGVzdCB3ZWFsdGguIEl0IGhhcyB0aGUgd2VhbHRoaWVzdCBhcmVhcyBidXQgYWxzbyBzb21lIGFyZWFzIGluIEhhcmxlbSBhbmQgbW9yZSB1cHRvd24gdGhhdCBhcmUgbGVzcyB3ZWFsdGh5IHRoYW4gbW9zdCBuZWlnaGJvcmhvb2RzIGluIFF1ZWVucywgQnJvb2tseW4gYW5kIHRoZSBCcm9ueC4NCg0KYGBge3J9DQpnX2FsbCA8LSBnZ21hcChtYXApICsgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsID0gYXZnLCB4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGRhdGEgPSBwbG90dGluZ196aXBzMSwgYWxwaGEgPSAwLjgpDQpnX2FsbCArIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBXZWFsdGggaW4gTmV3IFlvcmsiKSArIHNjYWxlX2ZpbGxfdmlyaWRpcygpDQpgYGANCg0KSGVyZSB3ZSBzZWUgYSBmdWxsIGRpc3RyaWJ1dGlvbiB1c2luZyB0aGUgdmlyaWRpcyBjb2xvciBzY2hlbWUuIFRoaXMgd2lsbCBiZSB1c2VkIGFzIHRoZSBiYXNlIGZvciB0aGUgb3ZlcmxhaWQgcGxvdHMgbGF0ZXIgb24gYW5kIHdlIHdpbGwgdGhlbiBmaWx0ZXIgYnkgZWFjaCBib3JvdWdoLg0KDQojIyMjIyBXZWFsdGggYnkgYm9yb3VnaCAtIEFkYW0NCg0KDQpgYGB7cn0NCnBsb3R0aW5nX3ppcHNfbWFuaGF0dGFuIDwtIHBsb3R0aW5nX3ppcHMxICU+JSBmaWx0ZXIoY2l0eS55ID09ICJOZXcgWW9yayIpDQpwbG90dGluZ196aXBzX2Jyb29rbHluIDwtIHBsb3R0aW5nX3ppcHMxICU+JSBmaWx0ZXIoY2l0eS55ID09ICJCcm9va2x5biIpDQpwbG90dGluZ196aXBzX2Jyb254IDwtIHBsb3R0aW5nX3ppcHMxICU+JSBmaWx0ZXIoY2l0eS55ID09ICJCcm9ueCIpDQpwbG90dGluZ196aXBzX3F1ZWVucyA8LSBwbG90dGluZ196aXBzMSAlPiUgZmlsdGVyKCEoY2l0eS55ICVpbiUgYygiTmV3IFlvcmsiLCAiQnJvb2tseW4iLCAiQnJvbngiLCAiU3RhdGVuIElzbGFuZCIpKSkNCiNwbG90dGluZ196aXBzX3N0YXRlbiA8LSBwbG90dGluZ196aXBzMSAlPiUgZmlsdGVyKGNpdHkueCAhJWluJSBjKCJNYW5oYXR0YW4iLCAiQnJvb2tseW4iLCAiQnJvbngiLCAiUXVlZW5zIFZpbGxhZ2UiKSkNCg0KYnJvbnhfbWFwIDwtIGdldF9tYXAoIkJyb254LCBOWSIsICBzb3VyY2UgPSAiZ29vZ2xlIiwgem9vbSA9IDEyLCBtYXB0eXBlPSJyb2FkbWFwIiwgY29sb3I9ImJ3IikNCmJyb29rbHluX21hcCA8LSBnZXRfbWFwKCJCcm9va2x5biwgTlkiLCAgc291cmNlID0gImdvb2dsZSIsIHpvb20gPSAxMiwgbWFwdHlwZT0icm9hZG1hcCIsIGNvbG9yPSJidyIpIA0KcXVlZW5zX21hcCA8LSBnZXRfbWFwKCJBc3RvcmlhLCBOWSIsICBzb3VyY2UgPSAiZ29vZ2xlIiwgem9vbSA9IDEyLCBtYXB0eXBlPSJyb2FkbWFwIiwgY29sb3I9ImJ3IikgDQpgYGANCg0KV2UgYXJlIG5vdCB1c2luZyBTdGF0ZW4gSXNsYW5kIHNpbmNlIHdlIGhhdmUgbm8gc2lkZXdhbGsgY2FmZSBkYXRhIGluIFN0YXRlbiBJc2xhbmQNCg0KYGBge3J9DQpnX21hbmhhdHRhbiA8LSBnZ21hcChtYXApICsgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsID0gaW5jb21lLiwgeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBkYXRhID0gcGxvdHRpbmdfemlwc19tYW5oYXR0YW4sIGFscGhhID0gMC41KQ0KZ19tYW5oYXR0YW4gKyBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgV2VhbHRoIGluIE1hbmhhdHRhbiIpICsgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9bWFuaGF0dGFuX25hbWVzLCBhZXMobWVhbl9sb24sIG1lYW5fbGF0LCBsYWJlbD1OZWlnaGJvcmhvb2QpLGZvbnRmYWNlPSJib2xkIiwgc2l6ZT0zKSArIHNjYWxlX2ZpbGxfdmlyaWRpcygpDQpgYGANCg0KVGhpcyBtYXAgc2hvdyB1cyB0aGUgd2VhbHRoIGluIE1hbmhhdHRhbiBhbG9uZSBieSB6aXBjb2RlLiBXZSBhcmUgbm93IHVzaW5nIHRoZSB2aXJpZGlzIGNvbG9yIHNjYWxlIHNvIHRoYXQgaXQgaXMgcGVyY2VwdHVhbGx5IHVuaWZvcm0gYW5kIGVhc2llciB0byBzZWUgZGlmZmVyZW5jZXMgaW4gY29sb3IuIFRoaXMgY2xlYXJseSBzaG93cyB0aGF0IHRoZSBpbmNvbWUgaXMgdmVyeSBsb3cgYXQgSGFybGVtIGFuZCBhYm92ZSwgaW5jbHVkaW5nIE1vcm5pbmdzaWRlIEhlaWdodHMuIFRoaXMgaXMgbGlrZWx5IGR1ZSB0byB0aGUgZmFjdCB0aGF0IGl0IGlzIGFsbW9zdCBleGNsdXNpdmVseSBzdHVkZW50cyBsaXZpbmcgaW4gTW9ybmluZ3NpZGUgSGVpZ2h0cyBhbmQgSGFybGVtIGlzIGNoZWFwZXIuIEFsc28sIGl0IGlzIGludGVyZXN0aW5nIHRvIHNlZSB0aGF0IExvd2VyIEVhc3QgU2lkZSBhbmQgaGFzIGEgc2ltaWxhciBpbmNvbWUgdG8gSGFybGVtLiBUaGUgd2VhbHRoaWVzdCBhcmVhcyBhcmUgYnkgZmFyIFVwcGVyIFdlc3QgU2lkZSBhbmQgVXBwZXIgRWFzdCBTaWRlLiBXZSBjYW4gYWxzbyBzZWUgdGhhdCB3ZSBhcmUgbWlzc2luZyB3aGF0IHNlZW1zIHRvIGJlIGRhdGEgZm9yIHR3byB6aXBjb2RlcyBpbiB0aGUgVXBwZXIgRWFzdCBzaWRlLiBUaGlzIGNvcnJlc3BvbmRzIHRvIHNvbWUgb2YgdGhlIGRhdGEgdGhhdCBNYXJpa2EgaGFkIGZvdW5kIHdhcyBtaXNzaW5nIGFuZCBzdGlsbCBkaWQgbm90IGFwcGVhciBpbiB0aGUgc3VwZXJ6aXAgZGF0YXNldC4NCg0KYGBge3IgZmlnd2lkdGg9MTB9DQpnX2Jyb29rbHluIDwtIGdnbWFwKGJyb29rbHluX21hcCkgKyBnZW9tX3BvbHlnb24oYWVzKGZpbGwgPSBpbmNvbWUuLCB4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGRhdGEgPSBwbG90dGluZ196aXBzX2Jyb29rbHluLCBhbHBoYSA9IDAuNSkNCmdfYnJvb2tseW4gKyBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgV2VhbHRoIGluIEJyb29rbHluIikgKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1icm9va2x5bl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykgKyBzY2FsZV9maWxsX3ZpcmlkaXMoKQ0KYGBgDQoNCkJ5IG9ic2VydmluZyB0aGUgaW5jb21lIGRpc3RyaWJ1dGlvbiBpbiBCcm9va2x5biwgd2Ugc2VlIHRoYXQgaW4gZ2VuZXJhbCwgdGhlIGFyZWFzIGNsb3NlciB0byBNYW5oYXR0YW4gaGF2ZSB0aGUgaGlnaGVzdCBpbmNvbWUgc3VjaCBhcyBCcm9va2x5biBIZWlnaHRzIGFuZCBDcm93biBIZWlnaHRzLg0KDQpgYGB7cn0NCmdfYnJvbnggPC0gZ2dtYXAoYnJvbnhfbWFwKSArIGdlb21fcG9seWdvbihhZXMoZmlsbCA9IGluY29tZS4sIHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZGF0YSA9IHBsb3R0aW5nX3ppcHNfYnJvbngsIGFscGhhID0gMC41KQ0KZ19icm9ueCArIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBXZWFsdGggaW4gQnJvbngiKSArIGdlb21fdGV4dF9yZXBlbChkYXRhPWJyb254X25hbWVzLCBhZXMobWVhbl9sb24sIG1lYW5fbGF0LCBsYWJlbD1OZWlnaGJvcmhvb2QpLGZvbnRmYWNlPSJib2xkIiwgc2l6ZT0zKSArIHNjYWxlX2ZpbGxfdmlyaWRpcygpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ19xdWVlbnMgPC0gZ2dtYXAocXVlZW5zX21hcCkgKyBnZW9tX3BvbHlnb24oYWVzKGZpbGwgPSBpbmNvbWUuLCB4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGRhdGEgPSBwbG90dGluZ196aXBzX3F1ZWVucywgYWxwaGEgPSAwLjUpDQpnX3F1ZWVucyArIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBXZWFsdGggaW4gUXVlZW5zIikgKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1xdWVlbnNfbmFtZXMsIGFlcyhtZWFuX2xvbiwgbWVhbl9sYXQsIGxhYmVsPU5laWdoYm9yaG9vZCksZm9udGZhY2U9ImJvbGQiLCBzaXplPTMpICsgc2NhbGVfZmlsbF92aXJpZGlzKCkNCmBgYA0KDQpTdXJwcmlzaW5nbHksIGl0IHNlZW1zIHRoYXQgaW4gUXVlZW5zLCBzb21lIG9mIHRoZSBoaWdoZXIgaW5jb21lIGFyZWFzIGFjdHVhbGx5IGFwcGVhciBmdXJ0aGVyIGF3YXkgZnJvbSBNYW5oYXR0YW4gbGlrZSBpbiBGbHVzaGluZyBhbmQgRm9yZXN0IEhpbGxzIGFuZCBSZWdvIFBhcmsuIEhvd2V2ZXIsIHRoZXJlIGFyZSBzb21lIGhpZ2hlciBpbmNvbWUgYXJlYXMgY2xvc2VyIHRvIE1hbmhhdHRhbiBhcyB3ZWxsIGxpa2UgRWFzdCBFbG1odXJzdC4gDQoNCiMjIyMgU2lkZXdhbGsgQ2FmZSBMaWNlbnNlIERhdGEgLSBNYXJpa2ENCkFueSBidXNpbmVzcyB0aGF0IG9wZXJhdGVzIGEgcG9ydGlvbiBvZiBhIHJlc3RhdXJhbnQgb24gYSBwdWJsaWMgc2lkZXdhbGsgbXVzdCBvYnRhaW4gYSBTaWRld2FsayBDYWZlIExpY2Vuc2UgZnJvbSBOZXcgWW9yIENpdHkuIFRoZXNlIGxpY2Vuc2VzIG11c3QgYmUgcmVuZXdlZCBldmVyeSB0d28geWVhcnMgYW5kIGZhbGwgaW50byB0aHJlZSBjYXRlZ29yaWVzOiBlbmNsb3NlZCwgdW5lbmNsb3NlZCwgb3Igc21hbGwgdW5lbmNsb3NlZCBzaWRld2FsayBjYWZlcy4gDQoNCiMjIyMjIEFuYWx5emluZyBieSBCb3JvdWdoDQoNCkZpcnN0LCB0byBoZWxwIGJldHRlciBvcmdhbml6ZSB0aGUgc2lkZXdhbGsgY2FmZSBsaWNlbnNlcyBieSBib3JvdWdoLCBJIGFkZGVkIGEgbmV3IGNvbHVtbiBjYWxsZWQgQk9ST1VHSCB0aGF0IGlzIHNldCB0byBNQU5IQVRUQU4sIEJST09LTFlOLCBCUk9OWCwgb3IgUVVFRU5TLiBJIGhhZCB0byBtYW51YWxseSBjaGVjayB0aGF0IG9ubHkgdGhlIGNpdGllcyBpbiBRdWVlbnMgaGFkIGJlZW4gY2FsbGVkIG91dCBzcGVjaWZpY2FsbHkgaW4gdGhlIENJVFkgY29sdW1uLCBzbyBpdCB3YXMgZWFzeSB0byBkaXN0aW5ndWlzaCB0aGVtIGZyb20gQlJPTlggb3IgQlJPT0tMWU4uDQpgYGB7cn0NCnNpZGV3YWxrcyA8LSBzaWRld2Fsa3MgJT4lIG11dGF0ZShCT1JPVUdIID0gaWZlbHNlKENJVFk9PSJORVcgWU9SSyJ8Q0lUWT09Ik5ldyBZb3JrIiwiTUFOSEFUVEFOIixpZmVsc2UoQ0lUWT09IkJST09LTFlOIiwiQlJPT0tMWU4iLGlmZWxzZShDSVRZPT0iQlJPTlgiLCJCUk9OWCIsIlFVRUVOUyIpKSkpDQpgYGANCg0KVG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBsaWNlbnNlcywgSSBoYXZlIHByb3ZpZGVkIGEgYmFyIGdyYXBoIGJ5IGJvcm91Z2guDQpgYGB7cn0NCmdncGxvdChzaWRld2Fsa3MsIGFlcyh4PWZjdF9pbmZyZXEoQk9ST1VHSCkpKStnZW9tX2JhcihhZXMoZmlsbD1CT1JPVUdIKSkrZ2d0aXRsZSgiRnJlcXVlbmN5IG9mIFNpZGV3YWxrIENhZmUgTGljZW5zZXMgYnkgQm9yb3VnaCIpK3hsYWIoIkJvcm91Z2giKSt5bGFiKCJGcmVxdWVuY3kiKQ0KYGBgDQpDbGVhcmx5IE1hbmhhdHRhbiBoYXMgdGhlIG1vc3QgbGljZW5zZSByZXF1ZXN0cywgZm9sbG93ZWQgYnkgQnJvb2tseW4sIHRoZW4gUXVlZW5zIGFuZCBmaW5hbGx5IEJyb254LiBTaW5jZSBhdCB0aGUgbW9tZW50IHdlIGRvbid0IGhhdmUgbmVpZ2hib3Job29kIGluZm9ybWF0aW9uIChldmVyeXRoaW5nIGluIE1hbmhhdHRhbiBpcyBqdXN0IGNsYXNzaWZpZWQgYXMgTmV3IFlvcmssIEJyb29rbHluIGhhcyBvbmx5IEJyb29rbHluLCBhbmQgQnJvbnggaGFzIG9ubHkgdGhlIGNpdHkgb2YgQnJvbngpLCB3ZSBjYW4gb25seSBkaXZlIGludG8gdGhlIFF1ZWVucyBkYXRhOg0KDQpgYGB7cn0NCnF1ZWVuc19jYWZlcyA8LSBzaWRld2Fsa3MgJT4lIGZpbHRlcihCT1JPVUdIPT0iUVVFRU5TIikNCmdncGxvdChxdWVlbnNfY2FmZXMsIGFlcyh4PWZjdF9pbmZyZXEoQ0lUWSkpKStnZW9tX2JhcihmaWxsPSJwdXJwbGUiKStnZ3RpdGxlKCJGcmVxdWVuY3kgb2YgU2lkZXdhbGsgQ2FmZSBMaWNlbnNlcyBpbiBRdWVlbnMiKSt4bGFiKCJDaXR5IC8gTmVpZ2hib3Job29kIikreWxhYigiRnJlcXVlbmN5IikrY29vcmRfZmxpcCgpDQoNCmBgYA0KDQpJbiBRdWVlbnMsIGEgbGFyZ2UgcGVyY2VudGFnZSBvZiBsaWNlbnNlIHJlcXVlc3RzIGNvbWUgZnJvbSBBc3RvcmlhLCBmb2xsb3dlZCBieSBMb25nIElzbGFuZCBDaXR5IGFuZCBGb3Jlc3QgSGlsbHMuIA0KDQpOZXh0LCBpbiBvcmRlciB0byBkbyBkYXRlIGNvbXBhcmlzb25zIHRvIGFzY2VydGFpbiB3aGljaCBhcmUgdGhlIG5ldyBhcHBsaWNhdGlvbnMgdnMuIHJlbmV3YWwgYXBwbGljYXRpb25zLCBJIGhhZCB0byBjb252ZXJ0IGNlcnRhaW4gZGF0ZSBmaWVsZHMgZnJvbSBzdHJpbmdzICh0aGV5IHdlcmUgcmVhZCBpbiBhcyBzdHJpbmcgZmFjdG9ycykgaW50byBkYXRlcy4NCg0KYGBge3J9DQpzaWRld2Fsa3MkRVhQSVJBVElPTl9EQVRFPC1hcy5EYXRlKHNpZGV3YWxrcyRFWFBJUkFUSU9OX0RBVEUsIGZvcm1hdD0iJW0vJWQvJVkiKQ0Kc2lkZXdhbGtzJEFQUF9TVEFUVVNfREFURTwtYXMuRGF0ZShzaWRld2Fsa3MkQVBQX1NUQVRVU19EQVRFLCBmb3JtYXQ9IiVtLyVkLyVZIikNCnNpZGV3YWxrcyRTVUJNSVRfREFURTwtYXMuRGF0ZShzaWRld2Fsa3MkU1VCTUlUX0RBVEUsIGZvcm1hdD0iJW0vJWQvJVkiKQ0KYGBgDQoNCiMjIyMjIExpY2Vuc2VzIGJ5IFN0YXR1cw0KDQpUaGUgbGlzdCBvZiBsaWNlbnNlcyBpbmNsdWRlcyBhY3RpdmUgbGljZW5zZXMsIGV4cGlyZWQgbGljZW5zZXMsIGxpY2Vuc2VzIGZvciBidXNpbmVzc2VzIHRoYXQgaGF2ZSBjbG9zZWQgKGFuZCBhcmUgbm93IGluYWN0aXZlKSwgbGljZW5zZXMgd2hpY2ggYXJlIHVwIGZvciByZW5ld2FsIGFzIHBhcnQgb2YgdGhlIHR3byB5ZWFyIHByb2Nlc3MsIG9yIG5ldyByZXF1ZXN0cyBmb3IgbGljZW5zZXMuIFRvIGJldHRlciBjbGFzc2lmeSB0aGVtLCBJIGNyZWF0ZWQgYSBuZXcgZmllbGQgY2FsbGVkIFNUQVRVU19DTEFTU0lGSUNBVElPTi4gVGhvc2UgbGljZW5zZXMgd2hpY2ggYXJlIHN0aWxsIGFjdGl2ZSBhbmQgbm90IHVwIGZvciByZW5ld2FsIGFyZSBjbGFzc2lmaWVkIGFzICJBQ1RJVkUiLiBUaG9zZSBsaWNlbnNlcyB0aGF0IGhhdmUgYmVlbiBzdWJtaXR0ZWQgZm9yIHJlbmV3YWwgKGVpdGhlciBiZWNhdXNlIHRoZWlyIGV4cGlyYXRpb24gZGF0ZSBpcyBsZXNzIHRoYW4gdGhlIGxhdGVzdCBhcHBsaWNhdGlvbiBkYXRhLCBvciB0aGF0IGFuIGFjdGl2ZSBsaWNlbnNlIGlzIHVwIGZvciByZXZpZXcpIGFyZSBjbGFzc2lmaWVkIGFzICJSRU5FV0FMIi4gVGhvc2UgbGljZW5zZXMgdGhhdCBhcmUgaW4gdGhlIHNoZWV0IGJ1dCBkbyBub3QgaGF2ZSBhIGxpY2Vuc2UgbnVtYmVyIGFyZSBjbGFzc2lmaWVkIGFzICJORVciLCBhbmQgdGhlIHJlc3QgYXJlIG1hcmtlZCBhcyAiT0xEIiB0byBlbmNvbXBhc3MgaW5hY3RpdmUgbGljZW5zZXMgdGhhdCBoYXZlIG5vdCBiZWVuIGFjdGVkIHVwb24uDQoNCmBgYHtyfQ0Kc2lkZXdhbGtzPC1zaWRld2Fsa3MgJT4lIG11dGF0ZShTVEFUVVNfQ0xBU1NJRklDQVRJT04gPSBpZmVsc2UoTElDX1NUQVRVUz09IkFjdGl2ZSIgJiAoQVBQX1NUQVRVUz09IkFwcGxpY2F0aW9uIEFwcHJvdmVkIiB8IEFQUF9TVEFUVVM9PSJBcHBsaWNhdGlvbiBSZXZpZXcgQ29tcGxldGVkIiksIkFDVElWRSIsaWZlbHNlKGlzLm5hKExJQ0VOU0VfTkJSKSwiTkVXIixpZmVsc2UoKEFQUF9TVEFUVVNfREFURT5FWFBJUkFUSU9OX0RBVEUgfCBEUFFBPT0iSXNzdWVkIFRlbXAgT3AgTGV0dGVyIikgfCAoTElDX1NUQVRVUz09IkFjdGl2ZSIgJiAoQVBQX1NUQVRVUz09IlBlbmRpbmcgUmV2aWV3IiB8IEFQUF9TVEFUVVM9PSJTdWJtaXR0ZWQiKSksIlJFTkVXQUwiLCJPTEQiKSkpKQ0KYGBgDQoNCk5vdyB0aGF0IHdlIGhhdmUgY2xhc3NpZmllZCB0aGUgc3RhdHVzIG9mIHRoZSBsaWNlbnNlcywgd2UgYXJlIGFibGUgdG8gc2VlIGhvdyB0aGVzZSBjbGFzc2lmaWNhdGlvbnMgZGlmZmVyIGJldHdlZW4gdGhlIGJvcm91Z2hzLiANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KHNpZGV3YWxrcykrZ2VvbV9tb3NhaWMoYWVzKHg9cHJvZHVjdChTVEFUVVNfQ0xBU1NJRklDQVRJT04sQk9ST1VHSCksZmlsbD1mYWN0b3IoU1RBVFVTX0NMQVNTSUZJQ0FUSU9OKSkpK2Nvb3JkX2ZsaXAoKStsYWJzKHg9IkJvcm91Z2giLHk9IkxpY2Vuc2UgU3RhdHVzIiwgZmlsbD0iTGljZW5zZSBEZXNpZ25hdGlvbiIpK2dndGl0bGUoIkJvcm91Z2hzIGJ5IExpY2Vuc2UgU3RhdHVzIikNCmBgYA0KVGhlIG1vc2FpYyBwbG90IHNob3dzIGhvdyBCcm9ueCBhbmQgQnJvb2tseW4gbWF5IGJlIGdldHRpbmcgbW9yZSBuZXcgbGljZW5zZSByZXF1ZXN0cyBhcyBhIHBlcmNlbnRhZ2Ugb2YgdG90YWwgbGljZW5zZXMuIEJyb254IGlzIGFsc28gZ2V0dGluZyB0aGUgaGlnaGVzdCBwZXJjZW50YWdlIG9mIHJlbmV3YWwgcmVxdWVzdHMgb3V0IG9mIGl0cyBpbmFjdGl2ZSBhbmQgYWN0aXZlIGxpY2Vuc2VzLiBXZSBjYW4gYWxzbyB0YWtlIGEgbG9vayBhdCB0aGUgbGljZW5zZSBkZXNpZ25hdGlvbnMgYnkgYm9yb3VnaDoNCg0KYGBge3J9DQpnZ3Bsb3Qoc2lkZXdhbGtzKStnZW9tX21vc2FpYyhhZXMoeD1wcm9kdWN0KEJPUk9VR0gsU1RBVFVTX0NMQVNTSUZJQ0FUSU9OKSxmaWxsPWZhY3RvcihCT1JPVUdIKSkpK2Nvb3JkX2ZsaXAoKStsYWJzKHg9IkxpY2Vuc2UgU3RhdHVzIix5PSJCb3JvdWdoIiwgZmlsbD0iQm9yb3VnaCIpK2dndGl0bGUoIkxpY2Vuc2UgU3RhdHVzIGJ5IEJvcm91Z2giKQ0KYGBgDQoNCkxvb2tpbmcgYXQgdGhlIGRhdGEgaW4gdGhpcyB3YXksIHlvdSBjYW4gc2VlIGhvdyBCcm9va2x5biBoYXMgdGhlIHNlY29uZC1tb3N0IG5ldyBsaWNlbnNlIHJlcXVlc3RzLCBidXQgaG93IE1hbmhhdHRhbiBzdGlsbCBkb21pbmF0ZXMgaW4gYWxsIGxpY2Vuc2Ugc3RhdHVzIGNhdGVnb3JpZXMuDQoNCiMjIyMjIE1hcHBpbmcgTGljZW5zZXMNCg0KV2UgY2FuIG1hcCB0aGUgZGF0YSB0byBoYXZlIGEgYmV0dGVyIHZpZXcgb2Ygd2hlcmUgdGhlIGRhdGFwb2ludHMgbGllLiBUbyBnZXQgYW4gb3ZlcmFsbCBwaWN0dXJlLCBJIHNlbGVjdGVkIGEgbWFwIGNlbnRlcmVkIG9uIExvbmcgSXNsYW5kIENpdHkgaW4gUXVlZW5zIHNvIHRoYXQgd2UgY2FuIGdldCBhIGdvb2QgdmlldyBvZiBib3RoIEJyb29rbHluIGFuZCBCcm9ueCBpbiBhZGRpdGlvbiB0byBNYW5oYXR0YW4uIA0KYGBge3J9DQptYXAgPC0gZ2V0X21hcCggbG9jYXRpb24gPSBjKC03My45NDg1NDI0LCA0MC43NDU0NTEzKSwgIHNvdXJjZSA9ICJnb29nbGUiLCB6b29tID0gMTEsIG1hcHR5cGU9InJvYWRtYXAiLCBjb2xvcj0iYnciKSANCmBgYA0KUGxvdHRpbmcgZWFjaCBvZiB0aGUgcmVzdGF1cmFudHMgY29sb3JlZCBieSB0aGVpciBib3JvdWdoLiBZb3UgY2FuIHNlZSBob3cgTWFuaGF0dGFuIGRvbWluYXRlcyBpbiB0aGUgbnVtYmVyIG9mIHNpZGV3YWxrIGNhZmVzLCBhbmQgaG93IHRoZSBzaWRld2FsayBjYWZlcyBpbiBCcm9va2x5biBhbmQgUXVlZW5zIGFyZSBsYXJnZWx5IGNvbmNlbnRyYXRlZCBpbiB0aGUgYXJlYXMgY2xvc2VyIHRvIE1hbmhhdHRhbi4NCmBgYHtyIGZpZy53aWR0aD0xMH0NCmdnbWFwKG1hcCkrZ2VvbV9wb2ludChhZXMoeD1MT05HSVRVREUseT1MQVRJVFVERSwgY29sb3I9Qk9ST1VHSCksZGF0YT1zaWRld2Fsa3MsIGFscGhhPTAuMykrZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIGFsbCBsaWNlbnNlcyAoaW5hY3RpdmUgb3IgYWN0aXZlKSBpbiBOWUMiKQ0KYGBgDQpFdmVuIHdpdGggYWxwaGEsIGl0IGlzIGRpZmZpY3VsdCB0byB0ZWxsIGV4YWN0bHkgd2hlcmUgdGhlIGxhcmdlc3QgY29uY2VudHJhdGlvbnMgb2Ygc2lkZXdhbGsgY2FmZXMgbGllLiBVc2luZyBhIGRlbnNpdHkgcGxvdCwgd2UgY2FuIGJldHRlciBzZWUgdGhlIGNvbmNlbnRyYXRpb24gb2Ygc2lkZXdhbGsgY2FmZXMgYXJvdW5kIG1pZC10byBsb3dlciBNYW5oYXR0YW4sIGFuZCB0aGUgY2x1c3RlcnMgaW4gQXN0b3JpYSBhbmQgV2lsbGlhbXNidXJnLiANCmBgYHtyIGZpZy53aWR0aD0xMH0NCmcgPC0gZ2dtYXAobWFwKStzdGF0X2RlbnNpdHkyZChhZXMoeD1MT05HSVRVREUseT1MQVRJVFVERSwgZmlsbD0uLmxldmVsLi4pLGRhdGE9c2lkZXdhbGtzLCBnZW9tPSJwb2x5Z29uIiwgYWxwaGE9MC4yKQ0KZytzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IixoaWdoPSJyZWQiKStnZ3RpdGxlKCJIZWF0bWFwIG9mIFNpZGV3YWxrIENhZmUgTGljZW5zZXMgaW4gTllDIikNCmBgYA0KVGhlIG5leHQgcXVlc3Rpb24gd2UgY2FuIGFzayBpcyB3aGV0aGVyIHRoZXJlIGFyZSBjbGVhciBwYXR0ZXJucyB0byB3aGVyZSBuZXcgbGljZW5zZSByZXF1ZXN0cyBhcmUgY29taW5nIGluIGZyb20sIHdoZXJlIHRoZXkgYXJlIGJlaW5nIHJlbmV3ZWQsIG9yIHdoZXJlIHRoZXkgaGF2ZSBleHBpcmVkIHdpdGhvdXQgcmVuZXdhbC4gDQpgYGB7ciBmaWcud2lkdGg9MTB9DQpnZ21hcChtYXApK2dlb21fcG9pbnQoYWVzKHg9TE9OR0lUVURFLHk9TEFUSVRVREUsIGNvbG9yPUJPUk9VR0gpLGRhdGE9c2lkZXdhbGtzLCBhbHBoYT0wLjQpK2ZhY2V0X3dyYXAoflNUQVRVU19DTEFTU0lGSUNBVElPTikrZ2d0aXRsZSgiU2lkZXdhbGsgQ2FmZSBMaWNlbnNlcyBieSBTdGF0dXMiKQ0KYGBgDQpIb3dldmVyLCBzaW5jZSB3ZSBhcmUgem9vbWVkIG91dCBhIGxvdCBhbmQgbG9va2luZyBhdCB0aGUgZW50aXJlIHNldCBvZiBsaWNlbnNlcywgaXQgaXMgZGlmZmljdWx0IHRvIHRlbGwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZGlzdHJpYnV0aW9ucyBvZiBsaWNlbnNlIHJlcXVlc3RzLiBGb3IgdGhpcyBhbmFseXNpcywgSSBhbSBvbmx5IGNvbmNlcm5lZCBhYm91dCBhY3RpdmUgbGljZW5zZXMgKHdoZXRoZXIgdGhleSBhcmUgYmVpbmcgcmVuZXdlZCBvciBub3QpLCBhbmQgbmV3IHJlcXVlc3RzIHRoYXQgaGF2ZSBub3QgeWV0IGJlZW4gYXBwcm92ZWQuIFRvIGRvIHRoaXMsIEkgY3JlYXRlZCBhbm90aGVyIFNUQVRVU19DTEFTU0lGSUNBVElPTiB0aGF0IGdyb3VwcyBhY3RpdmUgcmVuZXdhbCByZXF1ZXN0cyBpbnRvIHRoZSBBY3RpdmUgY2F0ZWdvcnksIGFuZCBzZXRzIGFsbCBvdGhlciBub24tbmV3IHJlcXVlc3RzIGFzICJpbmFjdGl2ZSIuIA0KYGBge3IgZmlnLndpZHRoPTEwfQ0Kc2lkZXdhbGtzIDwtIHNpZGV3YWxrcyAlPiUgbXV0YXRlKFNUQVRVU19DTEFTU0lGSUNBVElPTjI9aWZlbHNlKFNUQVRVU19DTEFTU0lGSUNBVElPTj09IkFDVElWRSIgfCAoU1RBVFVTX0NMQVNTSUZJQ0FUSU9OPT0iUkVORVdBTCIgJiBMSUNfU1RBVFVTPT0iQWN0aXZlIiksICJBQ1RJVkUiLCBpZmVsc2UoU1RBVFVTX0NMQVNTSUZJQ0FUSU9OPT0iTkVXIiwiTkVXIiwiT0xEIikpKQ0KbmV3X2FjdGl2ZSA8LSBzaWRld2Fsa3MgJT4lIGZpbHRlcihTVEFUVVNfQ0xBU1NJRklDQVRJT04yPT0iQUNUSVZFIiB8IFNUQVRVU19DTEFTU0lGSUNBVElPTjI9PSJORVciKQ0KZ2dtYXAobWFwKStnZW9tX3BvaW50KGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBjb2xvcj1CT1JPVUdIKSxkYXRhPW5ld19hY3RpdmUsIGFscGhhPTAuMykrZmFjZXRfd3JhcCh+U1RBVFVTX0NMQVNTSUZJQ0FUSU9OMikrZ2d0aXRsZSgiQWN0aXZlIGFuZCBOZXcgTGljZW5zZXMgaW4gTmV3IFlvcmsgQ2l0eSIpDQpgYGANClNpbmNlIHdlIGFyZSBxdWl0ZSB6b29tZWQgb3V0LCBpdCBpcyBkaWZmaWN1bHQgdG8gc2VlIHdoYXQgZXhhY3RseSBpcyBoYXBwZW5pbmcgd2l0aCB0aGUgTmV3IHJlcXVlc3RzLiBIb3dldmVyLCBpZiB3ZSB6b29tZWQgaW4sIHdlIHdvdWxkIGxvc2UgaW5mb3JtYXRpb24gYWJvdXQgdGhlIEJyb254IG9yIGxvd2VyIEJyb29rbHluLiBJbiBvcmRlciB0byBkZXRlcm1pbmUgd2hldGhlciB0aGVyZSBpcyBhbnkgdXNlZnVsIGluZm9ybWF0aW9uIHRoZXJlLCBJIGhhdmUgem9vbWVkIGluIG9uIHRoZSBCcm9ueCByZWdpb24uIA0KDQojIyMjIyBHZW9ncmFwaGljIEFuYWx5c2lzIC0gQnJvbngNCmBgYHtyfQ0KYnJvbnhfbWFwIDwtIGdldF9tYXAoIkJyb254LCBOWSIsICBzb3VyY2UgPSAiZ29vZ2xlIiwgem9vbSA9IDEyLCBtYXB0eXBlPSJyb2FkbWFwIiwgY29sb3I9ImJ3IikgDQpgYGANCmBgYHtyIGZpZy53aWR0aD0xMH0NCmdnbWFwKGJyb254X21hcCkrZ2VvbV9wb2ludChhZXMoeD1MT05HSVRVREUseT1MQVRJVFVERSwgY29sb3I9Qk9ST1VHSCksZGF0YT1uZXdfYWN0aXZlKStmYWNldF93cmFwKH5TVEFUVVNfQ0xBU1NJRklDQVRJT04yKStnZ3RpdGxlKCJBY3RpdmUgYW5kIE5ldyBMaWNlbnNlcyBpbiB0aGUgQnJvbngiKQ0KYGBgDQpTaW5jZSB0aGVyZSBhcmUgdmVyeSBmZXcgYWN0aXZlIG9yIG5ldyBsaWNlbnNlcyBpbiB0aGUgQnJvbngsIEkgZmVlbCBjb21mb3J0YWJsZSBpbiB6b29taW5nIGluIG9uIHRoZSByZXN0IG9mIE1hbmhhdHRhbiAvIEJyb29rbHluIGFuZCBRdWVlbnMgaW4gb3JkZXIgdG8gYmUgYWJsZSB0byBiZXR0ZXIgc2VlIHdoYXQgaXMgaGFwcGVuaW5nIGF0IHRoZSBleHBlbnNlIG9mIEJyb254LiANCg0KIyMjIyMgR2VvZ3JhcGhpYyBBbmFseXNpcyAtIE1hbmhhdHRhbg0KSSB3YW50IHRvIHRha2UgYSBjbG9zZXIgbG9vayBhdCB3aGF0IGlzIGhhcHBlbmluZyBpbiBNYW5oYXR0YW4uIEluIG9yZGVyIHRvIGRvIHRoaXMgYXQgYSBtb3JlIGdyYW51bGFyIGxldmVsLCBJIHdpbGwgdXNlIG15IG1lcmdlZCBkYXRhIHdpdGggb3VyIG1hc3Rlcl96aXAgZG9jdW1lbnQgd2hpY2ggbGlzdHMgdGhlIG5laWdoYm9yaG9vZHMgb2YgZWFjaCBCb3JvdWdoIGJ5IHppcCBjb2RlLiBJIGFsc28gcHVsbGVkIG91dCB0aGUgeWVhciBvZiB0aGUgc3VibWlzc2lvbiBvZiB0aGUgbGljZW5zZSBhcHBsaWNhdGlvbiBhbmQgc2F2ZWQgaXQgYXMgU1VCTUlUX1lFQVIuIA0KYGBge3J9DQptYXN0ZXJfemlwPC1yZWFkLmNzdigiRGF0YS96aXBzX21hc3Rlcl9ub19taXNzaW5nX25icmguY3N2Iiwgc3RyaXAud2hpdGU9VFJVRSkNCnNpZGV3YWxrc19uYmggPC0gbWVyZ2Uoc2lkZXdhbGtzLCBtYXN0ZXJfemlwLCBieT0iWklQIiwgYWxsLng9VFJVRSkNCg0Kc2lkZXdhbGtzX25iaCA8LSBzaWRld2Fsa3NfbmJoICU+JSBtdXRhdGUoU1VCTUlUX1lFQVI9eWVhcihTVUJNSVRfREFURSkpDQoNCnNpZGV3YWxrc19tYW5oYXR0YW5fYWN0aXZlIDwtIHNpZGV3YWxrc19uYmggJT4lIGZpbHRlcihCT1JPVUdIPT0iTUFOSEFUVEFOIiAmIChTVEFUVVNfQ0xBU1NJRklDQVRJT04yPT0iQUNUSVZFIiB8IFNUQVRVU19DTEFTU0lGSUNBVElPTjI9PSJORVciKSkNCg0KbWFuaGF0dGFuX21hcCA8LSBnZXRfbWFwKCJNYW5oYXR0YW4sIE5ZIiwgc291cmNlPSJnb29nbGUiLCBtYXB0eXBlPSJyb2FkbWFwIiwgem9vbT0xMiwgY29sb3I9ImJ3IikNCmBgYA0KSSBoYXZlIGFsc28gY2FsY3VsYXRlZCB0aGUgYXZlcmFnZSBsb2NhdGlvbnMgb2YgdGhlIG5laWdoYm9yaG9vZHMgaW4gZWFjaCBib3JvdWdoIGJhc2VkIG9uIHRoZWlyIGFzc2lnbmVkIFppcCBjb2Rlcy4gDQpgYGB7ciBmaWcud2lkdGg9MTB9DQoNCmdnbWFwKG1hbmhhdHRhbl9tYXApK2dlb21fcG9pbnQoYWVzKHg9TE9OR0lUVURFLCB5PUxBVElUVURFLCBjb2xvcj1uYmgpLCBkYXRhPXNpZGV3YWxrc19tYW5oYXR0YW5fYWN0aXZlKStnZ3RpdGxlKCJBbGwgYWN0aXZlIG9yIG5ldyBzaWRld2FsayBjYWZlcyBpbiBNYW5oYXR0YW4iKQ0KDQpzaWRld2Fsa3NfYnJvbng8LSBzaWRld2Fsa3NfbmJoICU+JSBmaWx0ZXIoQk9ST1VHSD09IkJST05YIikNCg0KYGBgDQpUaGlzIHZpZXcgc2hvd3MgYWxsIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgYWxsIGN1cnJlbnRseSBhY3RpdmUgb3IgbmV3IHNpZGV3YWxrIGNhZmVzIGluIE1hbmhhdHRhbi4gVG8gZ2V0IGEgYmV0dGVyIGlkZWEgb2YgZGVuc2l0eSwgd2UgY2FuIGFsc28gbG9vayBhdCBhIGhlYXRtYXAgb2YgdGhpcyB2aWV3LiANCg0KYGBge3IgZmlnLndpZHRoPTEwfQ0KZyA8LSBnZ21hcChtYW5oYXR0YW5fbWFwKStzdGF0X2RlbnNpdHkyZChhZXMoeD1MT05HSVRVREUseT1MQVRJVFVERSwgZmlsbD0uLmxldmVsLi4pLGRhdGE9c2lkZXdhbGtzX21hbmhhdHRhbl9hY3RpdmUsIGdlb209InBvbHlnb24iLCBhbHBoYT0wLjMpDQpnK3NjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSJ5ZWxsb3ciLGhpZ2g9InJlZCIpK2dndGl0bGUoIkhlYXRtYXAgb2YgU2lkZXdhbGsgQ2FmZSBMaWNlbnNlcyBpbiBNYW5oYXR0YW4iKStnZW9tX3RleHRfcmVwZWwoZGF0YT1tYW5oYXR0YW5fbmFtZXMsIGFlcyhtZWFuX2xvbiwgbWVhbl9sYXQsIGxhYmVsPU5laWdoYm9yaG9vZCksZm9udGZhY2U9ImJvbGQiLCBzaXplPTMpDQoNCmBgYA0KDQpUaGlzIGhlYXRtYXAgc2hvd3MgdGhlIGhpZ2hlc3QgY29uY2VudHJhdGlvbiBvZiBzaWRld2FsayBjYWZlcyBpbiBHcmVlbndpY2ggVmlsbGFnZSBhbmQgTm9Iby4gVG8gc2VlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYWN0aXZlIGFuZCBuZXcgbGljZW5zZXMsIHdlIGNhbiBmYWNldCBiYXNlZCBvbiBvdXIgcHJldmlvdXNseSBjYWxjdWxhdGVkIGNhdGVnb3JpemF0aW9uLg0KDQpgYGB7ciBmaWcud2lkdGg9MTB9DQpnIDwtIGdnbWFwKG1hbmhhdHRhbl9tYXApK3N0YXRfZGVuc2l0eTJkKGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBmaWxsPS4ubGV2ZWwuLiksZGF0YT1zaWRld2Fsa3NfbWFuaGF0dGFuX2FjdGl2ZSwgZ2VvbT0icG9seWdvbiIsIGFscGhhPTAuMykrZmFjZXRfd3JhcCh+U1RBVFVTX0NMQVNTSUZJQ0FUSU9OMikNCmcrc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InllbGxvdyIsaGlnaD0icmVkIikrZ2d0aXRsZSgiSGVhdG1hcCBvZiBTaWRld2FsayBDYWZlIExpY2Vuc2VzIGluIE1hbmhhdHRhbiIpK2dlb21fdGV4dF9yZXBlbChkYXRhPW1hbmhhdHRhbl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykNCmBgYA0KVGhlIGhlYXRtYXAgdmlldyBvZiB0aGUgbWFwIGlzIG5vdCBhIGdyZWF0IG9uZSAtIGl0IGlzIGRpZmZpY3VsdCB0byB0ZWxsIHRoZSBkaWZmZXJlbmNlcyBvbiB0aGUgc2FtZSBzY2FsZXMgaW4gYSBoZWF0bWFwLiBMb29raW5nIGF0IHRoZSBzYW1lIHZpZXcsIGJ1dCBieSB1c2luZyBnZW9tX3BvaW50IGFnYWluLCB3ZSBnZXQgdGhlIGZvbGxvd2luZyB2aWV3OiANCmBgYHtyIGZpZy53aWR0aD0xMH0NCmdnbWFwKG1hbmhhdHRhbl9tYXApK2dlb21fcG9pbnQoYWVzKHg9TE9OR0lUVURFLHk9TEFUSVRVREUsIGNvbG9yPW5iaCksZGF0YT1zaWRld2Fsa3NfbWFuaGF0dGFuX2FjdGl2ZSkrZmFjZXRfd3JhcCh+U1RBVFVTX0NMQVNTSUZJQ0FUSU9OMikrZ2d0aXRsZSgiQWN0aXZlIGFuZCBOZXcgTGljZW5zZXMgaW4gTWFuaGF0dGFuIikgKyBndWlkZXMoY29sb3I9RkFMU0UpK2dlb21fdGV4dF9yZXBlbChkYXRhPW1hbmhhdHRhbl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykNCmBgYA0KT25jZSBhZ2FpbiwgdGhlIGRhdGEgaXMgbm90IHRlbGxpbmcgdXMgdG9vIG11Y2ggc2luY2UgdGhlcmUgYXJlIHZlcnkgZmV3IG5ldyByZXF1ZXN0cy4gUGVyaGFwcyBhIGJldHRlciB3YXkgdG8gY2F0ZWdvcml6ZSB0aGlzIGRhdGEgaXMgdG8gbG9vayBhdCB0aGUgeWVhcnMgdGhhdCBhcHBsaWNhdGlvbnMgd2VyZSBzdWJtaXR0ZWQsIHRoZXJlZm9yZSBpZ25vcmluZyB3aGV0aGVyIGEgbGljZW5zZSBpcyBjdXJyZW50bHkgYWN0aXZlIG9yIG5vdC4NCg0KYGBge3J9DQp5ZWFyX2J4cDwtZ2dwbG90KHNpZGV3YWxrc19uYmgsIGFlcyh5PVNVQk1JVF9ZRUFSLHg9MSkpK2dlb21fYm94cGxvdCgpK2dndGl0bGUoIkJveHBsb3Qgb2YgU3VibWlzc2lvbiBZZWFycyIpDQp5ZWFyX2JhcjwtZ2dwbG90KHNpZGV3YWxrc19uYmgsYWVzKHg9U1VCTUlUX1lFQVIpKStnZW9tX2JhcigpK2dndGl0bGUoIkJhciBHcmFwaCBvZiBTdWJtaXNzaW9uIFllYXJzIikNCmdyaWQuYXJyYW5nZSh5ZWFyX2J4cCwgeWVhcl9iYXIsIG5jb2w9MikNCmBgYA0KDQpTaW5jZSB0aGVyZSBhcmUgdmVyeSBmZXcgc3VibWlzc2lvbnMgYmV0d2VlbiAyMDAwIGFuZCAyMDE0LCBJIHdpbGwgYmUgcmVtb3ZpbmcgdGhlc2Ugb3V0bGllcnMgYXMgcG90ZW50aWFsIG1pc3Rha2VzIHdoZXJlIHRoZSBzdWJtaXQgZGF0ZXMgd2VyZSBub3QgdXBkYXRlZCBvbmNlIHRoZSBsaWNlbnNlcyB3ZXJlIHJlbmV3ZWQuIE5vdyB3ZSBjYW4gbG9vayBhdCBhIG1vc2FpYyBwbG90IG9mIHRoZSBkaWZmZXJlbnQgbmVpZ2hib3Job29kcyBhbmQgdGhlIHllYXJzIHRoYXQgdGhlIGxpY2Vuc2VzIHdlcmUgcmVxdWVzdGVkLg0KDQpgYGB7cn0NCnNpZGV3YWxrc19uYmg8LSBzaWRld2Fsa3NfbmJoICU+JSBmaWx0ZXIoU1VCTUlUX1lFQVI+MjAxNCkNCg0Kc2lkZXdhbGtzX21hbmhhdHRhbjwtIHNpZGV3YWxrc19uYmggJT4lIGZpbHRlcihCT1JPVUdIPT0iTUFOSEFUVEFOIikNCg0KbWFuaGF0dGFuX2NvdW50cyA8LSBzaWRld2Fsa3NfbWFuaGF0dGFuICU+JSBncm91cF9ieShuYmgsIFNVQk1JVF9ZRUFSKSAlPiUgc3VtbWFyaXNlKGNvdW50PW4oKSkNCg0KbWFuaGF0dGFuX2NvdW50cyRTVUJNSVRfWUVBUjwtYXMuZmFjdG9yKG1hbmhhdHRhbl9jb3VudHMkU1VCTUlUX1lFQVIpDQoNCm1hbmhhdHRhbl9jb3VudHMkU1VCTUlUX1lFQVI8LWZhY3RvcihtYW5oYXR0YW5fY291bnRzJFNVQk1JVF9ZRUFSLCBjKCIyMDE3IiwgIjIwMTYiLCIyMDE1IikpDQoNCmdncGxvdChtYW5oYXR0YW5fY291bnRzLCBhZXMoeD1yZW9yZGVyKG5iaCwgY291bnQpLCB5PWNvdW50LCBmaWxsPVNVQk1JVF9ZRUFSKSkrZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpK2Nvb3JkX2ZsaXAoKSt4bGFiKCJOZWlnaGJvcmhvb2QiKStnZ3RpdGxlKCJMaWNlbnNlIFJlcXVlc3RzIHBlciBOZWlnaGJvcmhvb2QgYW5kIFllYXIiKQ0KYGBgDQpJdCBtYWtlcyBzZW5zZSB0aGF0IDIwMTYgd291bGQgaGF2ZSBtb3JlIGFwcGxpY2F0aW9ucyB0aGFuIDIwMTUsIHNpbmNlIG1hbnkgb2YgdGhlIDIwMTUgYXBwbGljYXRpb25zIGhhdmUgYmVlbiByZW5ld2VkIGJ5IG5vdy4gSXQgbG9va3MgbGlrZSBHcmVlbndpY2ggVmlsbGFnZSwgVXBwZXIgV2VzdCBTaWRlLCBhbmQgVXBwZXIgRWFzeSBTaWRlIGNvbnNpc3RlbnRseSBoYXZlIHRoZSBoaWdoZXN0IG51bWJlciBvZiByZXF1ZXN0cyB0aHJvdWdob3V0IHRoZSB0aHJlZSB5ZWFycy4gSG93ZXZlciwgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhcmVhcywgd2UgY2FuIGNyZWF0ZSB0aHJlZSBzZXBhcmF0ZSBoZWF0bWFwcy4NCg0KYGBge3J9DQptYXAyMDE1IDwtIGdnbWFwKG1hbmhhdHRhbl9tYXApK3N0YXRfZGVuc2l0eTJkKGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBmaWxsPS4ubGV2ZWwuLiksZGF0YT1zaWRld2Fsa3NfbWFuaGF0dGFuJT4lZmlsdGVyKFNVQk1JVF9ZRUFSPT0iMjAxNSIpLCBnZW9tPSJwb2x5Z29uIiwgYWxwaGE9MC4zKStzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IixoaWdoPSJyZWQiKStnZ3RpdGxlKCJNYW5oYXR0YW4gTGljZW5zZXMgQXBwbGllZCBmb3IgaW4gMjAxNSIpK2dlb21fdGV4dF9yZXBlbChkYXRhPW1hbmhhdHRhbl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykNCm1hcDIwMTUNCmBgYA0KYGBge3J9DQptYXAyMDE2PC1nZ21hcChtYW5oYXR0YW5fbWFwKStzdGF0X2RlbnNpdHkyZChhZXMoeD1MT05HSVRVREUseT1MQVRJVFVERSwgZmlsbD0uLmxldmVsLi4pLGRhdGE9c2lkZXdhbGtzX21hbmhhdHRhbiU+JWZpbHRlcihTVUJNSVRfWUVBUj09IjIwMTYiKSwgZ2VvbT0icG9seWdvbiIsIGFscGhhPTAuMykrc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InllbGxvdyIsaGlnaD0icmVkIikrZ2d0aXRsZSgiTWFuaGF0dGFuIExpY2Vuc2VzIEFwcGxpZWQgZm9yIGluIDIwMTYiKStnZW9tX3RleHRfcmVwZWwoZGF0YT1tYW5oYXR0YW5fbmFtZXMsIGFlcyhtZWFuX2xvbiwgbWVhbl9sYXQsIGxhYmVsPU5laWdoYm9yaG9vZCksZm9udGZhY2U9ImJvbGQiLCBzaXplPTMpDQptYXAyMDE2DQpgYGANCg0KYGBge3J9DQptYXAyMDE3IDwtIGdnbWFwKG1hbmhhdHRhbl9tYXApK3N0YXRfZGVuc2l0eTJkKGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBmaWxsPS4ubGV2ZWwuLiksZGF0YT1zaWRld2Fsa3NfbWFuaGF0dGFuJT4lZmlsdGVyKFNVQk1JVF9ZRUFSPT0iMjAxNyIpLCBnZW9tPSJwb2x5Z29uIiwgYWxwaGE9MC4zKStzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ieWVsbG93IixoaWdoPSJyZWQiKStnZ3RpdGxlKCJNYW5oYXR0YW4gTGljZW5zZXMgQXBwbGllZCBmb3IgaW4gMjAxNyIpK2dlb21fdGV4dF9yZXBlbChkYXRhPW1hbmhhdHRhbl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykNCm1hcDIwMTcNCg0KYGBgDQpBY3Jvc3MgYWxsIHRocmVlIHllYXJzLCB5b3UgY2FuIHNlZSBob3cgdGhlIGNvbmNlbnRyYXRlZCBhcmVhIG9mIGxpY2Vuc2VzIHJlbWFpbnMgaW4gdGhlIEdyZWVud2ljaCBWaWxsYWdlIGFyZWEuIEhvd2V2ZXIsIGl0IGxvb2tzIGxpa2UgdGhlIGNvbmNlbnRyYXRpb24gb2YgYXBwbGljYW50cyBpbiB0aGUgVXBwZXIgV2VzdCBTaWRlIGluIDIwMTcgaGFzIGRyb3BwZWQuIFRoaXMgbWFwIGlzIG1vcmUgdXNlZnVsIHRoYW4gdGhlIGJhciBjaGFydCBiZWNhdXNlIGl0IHNob3dzIHRoZSBsb2NhdGlvbiBvZiB0aGUgY2FmZXMgdGhhdCBtYXkgYmUgc3RyYWRkbGluZyB0d28gbmVpZ2hib3Job29kcyAtIGluZm9ybWF0aW9uIHRoYXQgaXMgbm90IGFwcGFyZW50IGZyb20gdGhlIGdyb3VwZWQgYmFyIGNoYXJ0LiANCg0KV2UgY2FuIGRvIHRoZSBzYW1lIHNvcnQgb2YgYW5hbHlzaXMgaW4gYWxsIGZvdXIgYm9yb3VnaHMgdGhhdCB3ZSBoYXZlIHNpZGV3YWxrIGNhZmUgaW5mb3JtYXRpb24gYWJvdXQuIFRvIG1ha2UgdGhpcyBhbmFseXNpcyBlYXNpZXIgdG8gdmlldywgSSBoYXZlIGNyZWF0ZWQgYSBzaGlueSBhcHAgd2hpY2ggY2FuIGJlIGZvdW5kIGJ5IGZvbGxvd2luZyB0aGlzIGxpbms6IFtTaGlueUNhZmVdKGh0dHBzOi8vbWFyaWthbG9obXVzLnNoaW55YXBwcy5pby9jYWZlc2hpbnkvKQ0KDQpgYGB7cn0NCnNpZGV3YWxrc19icm9va2x5biA8LSBzaWRld2Fsa3NfbmJoICU+JSBmaWx0ZXIoQk9ST1VHSD09IkJST09LTFlOIikNCnNpZGV3YWxrc19xdWVlbnMgPC0gc2lkZXdhbGtzX25iaCAlPiUgZmlsdGVyKEJPUk9VR0g9PSJRVUVFTlMiKQ0Kc2lkZXdhbGtzX2Jyb254IDwtIHNpZGV3YWxrc19uYmggJT4lIGZpbHRlcihCT1JPVUdIPT0iQlJPTlgiKQ0KDQpicm9va2x5bl9tYXAgPC0gZ2V0X21hcCgiQnJvb2tseW4sIE5ZIiwgIHNvdXJjZSA9ICJnb29nbGUiLCB6b29tID0gMTIsIG1hcHR5cGU9InJvYWRtYXAiLCBjb2xvcj0iYnciKSANCnF1ZWVuc19tYXAgPC0gZ2V0X21hcCgiQXN0b3JpYSwgTlkiLCAgc291cmNlID0gImdvb2dsZSIsIHpvb20gPSAxMiwgbWFwdHlwZT0icm9hZG1hcCIsIGNvbG9yPSJidyIpIA0KYGBgDQpUaGUgU2hpbnkgYXBwIGhhcyBhIGJyaWVmIGJsdXJiIGV4cGxhaW5pbmcgd2hhdCBlYWNoIGJvcm91Z2ggY2FuIHRlbGwgdXMgYWJvdXQgdGhlIG1vdmVtZW50IG9mIHNpZGV3YWxrIGNhZmUgYXBwbGljYXRpb25zLg0KDQpOZXh0LCBJIGFkZGVkIEFkYW0ncyBpbmNvbWUgZGlzdHJpYnV0aW9uIHBsb3lnb25zIHRvIHRoZSBiYWNrZ3JvdW5kIG9mIHRoZXNlIG1hcHMgdG8gc2VlIHdoYXQgdGhlIHJlbGF0aW9uc2hpcCBpcyB0byBpbmNvbWUuIA0KDQpgYGB7ciBmaWcud2lkdGg9MTB9DQpnZ21hcChtYW5oYXR0YW5fbWFwKStnZW9tX3BvbHlnb24oYWVzKGZpbGwgPSBpbmNvbWUuLCB4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGRhdGEgPSBwbG90dGluZ196aXBzX21hbmhhdHRhbiwgYWxwaGEgPSAwLjcpK2dlb21fcG9pbnQoYWVzKHg9TE9OR0lUVURFLHk9TEFUSVRVREUsIGNvbG9yPSJwdXJwbGUiKSxkYXRhPXNpZGV3YWxrc19tYW5oYXR0YW4sIHNpemU9MSkrIHNjYWxlX2ZpbGxfdmlyaWRpcygpK2dlb21fdGV4dF9yZXBlbChkYXRhPW1hbmhhdHRhbl9uYW1lcywgYWVzKG1lYW5fbG9uLCBtZWFuX2xhdCwgbGFiZWw9TmVpZ2hib3Job29kKSxmb250ZmFjZT0iYm9sZCIsIHNpemU9MykrZ3VpZGVzKGNvbG9yPUYpDQpgYGANCk1hbmhhdHRhbiBoYXMgYSBwcmV0dHkgY2xlYXIgY2x1c3RlcmluZyBvZiBzaWRld2FsayBjYWZlcyBpbiB0aGUgaGlnaGVyIGluY29tZSBhcmVhcywgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIExvd2VyIE1haGF0dGFuIGFuZCB0aGUgRmluYW5jaWFsIERpc3RyaWN0Lg0KDQpgYGB7ciBmaWcud2lkdGg9MTB9DQpnZ21hcChicm9va2x5bl9tYXApK2dlb21fcG9seWdvbihhZXMoZmlsbCA9IGluY29tZS4sIHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZGF0YSA9IHBsb3R0aW5nX3ppcHNfYnJvb2tseW4sIGFscGhhID0gMC43KStnZW9tX3BvaW50KGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBjb2xvcj0icHVycGxlIiksZGF0YT1zaWRld2Fsa3NfYnJvb2tseW4sIHNpemU9MSkrIHNjYWxlX2ZpbGxfdmlyaWRpcygpK2d1aWRlcyhjb2xvcj1GQUxTRSkNCmBgYA0KVGhlIG1pc3NpbmcgZGF0YSBpbiBXaWxsaWFtc2J1cmcgaXMgaGluZGVyaW5nIHRoaXMgYW5hbHlzaXMsIGJ1dCB3ZSBkbyBzZWUgaG93IHRoZSBhdmVyYWdlIGluY29tZSBpbiBCcm9va2x5biBIZWlnaHRzLCBQYXJrIFNsb3BlLCBhbmQgUmVkIEhvb2sgY29ycmVsYXRlIHdpdGggbW9yZSBzaWRld2FsayBjYWZlcy4NCg0KYGBge3IgZmlnLndpZHRoPTEwfQ0KZ2dtYXAocXVlZW5zX21hcCkrZ2VvbV9wb2x5Z29uKGFlcyhmaWxsID0gaW5jb21lLiwgeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBkYXRhID0gcGxvdHRpbmdfemlwc19xdWVlbnMsIGFscGhhID0gMC43KStnZW9tX3BvaW50KGFlcyh4PUxPTkdJVFVERSx5PUxBVElUVURFLCBjb2xvcj0icHVycGxlIiksZGF0YT1zaWRld2Fsa3NfcXVlZW5zLCBzaXplPTEpKyBzY2FsZV9maWxsX3ZpcmlkaXMoKStnZW9tX3RleHRfcmVwZWwoZGF0YT1xdWVlbnNfbmFtZXMsIGFlcyhtZWFuX2xvbiwgbWVhbl9sYXQsIGxhYmVsPU5laWdoYm9yaG9vZCksZm9udGZhY2U9ImJvbGQiLCBzaXplPTMpK2d1aWRlcyhjb2xvcj1GKQ0KYGBgDQpBc3RvcmlhIGFuZCBMb25nIElzbGFuZCBDaXR5IGFyZSBpbnRlcmVzdGluZyBjYXNlcyB3aXRoIHJlbGF0aXZlbHkgbG93IGF2ZXJhZ2UgaW5jb21lcyBidXQgYSBoaWdoIGNvbmNlbnRyYXRpb24gb2Ygc2lkZXdhbGsgY2FmZXMuIFRoZSBvdGhlciBhcmVhIHRvIHRoZSBzb3V0aC1lYXN0LCBhcm91bmQgUGFya3NpZGUgYW5kIEZvcmVzdCBIaWxscywgaGFzIGEgaGlnaCBjb25jZW50cmF0aW9uIG9mIGNhZmVzIGFuZCBoaWdoIGluY29tZS4NCmBgYHtyIGZpZy53aWR0aD0xMH0NCmdnbWFwKGJyb254X21hcCkrZ2VvbV9wb2x5Z29uKGFlcyhmaWxsID0gaW5jb21lLiwgeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBkYXRhID0gcGxvdHRpbmdfemlwc19icm9ueCwgYWxwaGEgPSAwLjcpK2dlb21fcG9pbnQoYWVzKHg9TE9OR0lUVURFLHk9TEFUSVRVREUsIGNvbG9yPSJwdXJwbGUiKSxkYXRhPXNpZGV3YWxrc19icm9ueCwgc2l6ZT0xKSsgc2NhbGVfZmlsbF92aXJpZGlzKCkrZ2VvbV90ZXh0X3JlcGVsKGRhdGE9YnJvbnhfbmFtZXMsIGFlcyhtZWFuX2xvbiwgbWVhbl9sYXQsIGxhYmVsPU5laWdoYm9yaG9vZCksZm9udGZhY2U9ImJvbGQiLCBzaXplPTMpK2d1aWRlcyhjb2xvcj1GKQ0KYGBgDQpXaXRoIHRoZSB2ZXJ5IGxvdyBudW1iZXIgb2YgQnJvbnggZGF0YXBvaW50cywgaXQgaXMgbm90IGVhc3kgdG8gc2VlIGEgcmVhbHRpb25zaGlwIGJldHdlZW4gaW5jb21lIGFuZCB3aGVyZSB0aGUgc2lkZXdhbGsgY2FmZXMgYXJlIGxvY2F0ZWQuDQoNCmBgYHtyfQ0KbWVhbl9pbmNvbWVzIDwtIHBsb3R0aW5nX3ppcHMxICU+JSBncm91cF9ieShaSVApICU+JSBzdW1tYXJpc2UobWVhbl9pbmNvbWU9bWVhbihpbmNvbWUuKSkNCnppcF9zaWRld2FsayA8LSBzaWRld2Fsa3NfbmJoICU+JSBncm91cF9ieShaSVAsIEJPUk9VR0gpICU+JSBzdW1tYXJpc2Uobl9jYWZlcz1uKCkpDQpzdW1tYXJ5X2NhZmUgPC0gbWVyZ2UoemlwX3NpZGV3YWxrLCBtZWFuX2luY29tZXMsIGJ5PSJaSVAiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChzdW1tYXJ5X2NhZmUsIGFlcyh4PW1lYW5faW5jb21lLCB5PW5fY2FmZXMpKStnZW9tX3BvaW50KCkreGxhYigiQXZlcmFnZSBJbmNvbWUgKGluIFRob3VzYW5kcyBvZiBEb2xsYXJzKSIpK3lsYWIoIk51bWJlciBvZiBTaWRld2FsayBDYWZlcyIpK2dlb21fc21vb3RoKG1ldGhvZD1sbSkgDQpgYGANCmBgYHtyfQ0KY2FmZS5sbSA9IGxtKG1lYW5faW5jb21lfm5fY2FmZXMsIGRhdGE9c3VtbWFyeV9jYWZlKSANCnN1bW1hcnkoY2FmZS5sbSkkci5zcXVhcmVkDQpgYGANCk9uIGFsbCB0aGUgZGF0YSwgdGhlcmUgaXMgcHJldHR5IHdlYWsgY29ycmVsYXRpb24gYmV0d2VlbiBpbmNvbWUgYW5kIG51bWJlciBvZiBzaWRld2FsayBjYWZlcywgd2l0aCBhbiByLXNxdWFyZXMgb2YgMC4xOTYuDQoNCmBgYHtyfQ0KZ2dwbG90KHN1bW1hcnlfY2FmZSwgYWVzKHg9bWVhbl9pbmNvbWUsIHk9bl9jYWZlcykpK2dlb21fcG9pbnQoKSt4bGFiKCJBdmVyYWdlIEluY29tZSAoaW4gVGhvdXNhbmRzIG9mIERvbGxhcnMpIikreWxhYigiTnVtYmVyIG9mIFNpZGV3YWxrIENhZmVzIikrZ2VvbV9zbW9vdGgobWV0aG9kPWxtKStmYWNldF93cmFwKH5CT1JPVUdILCBzY2FsZXM9ImZyZWUiKQ0KYGBgDQoNCkZhY2V0aW5nIGJ5IEJvcm91Z2gsIHdlIGNhbiBzZWUgaG93IHRoZSByZWxhdGlvbnNoaXAgaXMgYWN0dWFsbHkgbmVnYXRpdmUgaW4gQnJvbnggYW5kIFF1ZWVucywgYnV0IHBvc2l0aXZlIGluIE1hbmhhdHRhbiBhbmQgQnJvb2tseW4gKHdpdGggdGhlIHN0cm9uZ2VzdCByZWxhdGlvbnNoaXAgaW4gTWFuaGF0dGFuKS4gDQoNCg0KIyMjIyBMaXF1b3IgTGljZW5zZSBEYXRhIC0gUm9oYW4NCg0KIyMgNi4gQ29uY2x1c2lvbiAtIFJvaGFu